Просмотр исходного кода

Merge branch 'datawhalechina:main' into BRANCH-COMQUESTION

Anatole:-P 6 месяцев назад
Родитель
Сommit
d0784711ef
100 измененных файлов с 19988 добавлено и 0 удалено
  1. 107 0
      Co-creation-projects/1zrj-DataAnalysisAgent/README.md
  2. BIN
      Co-creation-projects/1zrj-DataAnalysisAgent/data/simple_data.xls
  3. 837 0
      Co-creation-projects/1zrj-DataAnalysisAgent/main.ipynb
  4. 99 0
      Co-creation-projects/1zrj-DataAnalysisAgent/output/echarts.html
  5. 62 0
      Co-creation-projects/1zrj-DataAnalysisAgent/output/report.md
  6. 12 0
      Co-creation-projects/1zrj-DataAnalysisAgent/requirements.txt
  7. 58 0
      Co-creation-projects/Apricity-InnocoreAI/.env.example
  8. 72 0
      Co-creation-projects/Apricity-InnocoreAI/.gitignore
  9. 215 0
      Co-creation-projects/Apricity-InnocoreAI/FEATURES.md
  10. 89 0
      Co-creation-projects/Apricity-InnocoreAI/QUICKSTART.md
  11. 266 0
      Co-creation-projects/Apricity-InnocoreAI/README.md
  12. 329 0
      Co-creation-projects/Apricity-InnocoreAI/USAGE_GUIDE.md
  13. 14 0
      Co-creation-projects/Apricity-InnocoreAI/__init__.py
  14. 19 0
      Co-creation-projects/Apricity-InnocoreAI/agents/__init__.py
  15. 173 0
      Co-creation-projects/Apricity-InnocoreAI/agents/base.py
  16. 417 0
      Co-creation-projects/Apricity-InnocoreAI/agents/coach.py
  17. 407 0
      Co-creation-projects/Apricity-InnocoreAI/agents/controller.py
  18. 353 0
      Co-creation-projects/Apricity-InnocoreAI/agents/hunter.py
  19. 416 0
      Co-creation-projects/Apricity-InnocoreAI/agents/miner.py
  20. 610 0
      Co-creation-projects/Apricity-InnocoreAI/agents/validator.py
  21. 11 0
      Co-creation-projects/Apricity-InnocoreAI/api/__init__.py
  22. 164 0
      Co-creation-projects/Apricity-InnocoreAI/api/main.py
  23. 7 0
      Co-creation-projects/Apricity-InnocoreAI/api/routes/__init__.py
  24. 567 0
      Co-creation-projects/Apricity-InnocoreAI/api/routes/analysis.py
  25. 330 0
      Co-creation-projects/Apricity-InnocoreAI/api/routes/citations.py
  26. 109 0
      Co-creation-projects/Apricity-InnocoreAI/api/routes/papers.py
  27. 299 0
      Co-creation-projects/Apricity-InnocoreAI/api/routes/tasks.py
  28. 132 0
      Co-creation-projects/Apricity-InnocoreAI/api/routes/users.py
  29. 304 0
      Co-creation-projects/Apricity-InnocoreAI/api/routes/workflow.py
  30. 383 0
      Co-creation-projects/Apricity-InnocoreAI/api/routes/writing.py
  31. 16 0
      Co-creation-projects/Apricity-InnocoreAI/core/__init__.py
  32. 153 0
      Co-creation-projects/Apricity-InnocoreAI/core/config.py
  33. 303 0
      Co-creation-projects/Apricity-InnocoreAI/core/database.py
  34. 50 0
      Co-creation-projects/Apricity-InnocoreAI/core/exceptions.py
  35. 130 0
      Co-creation-projects/Apricity-InnocoreAI/core/llm_adapter.py
  36. 281 0
      Co-creation-projects/Apricity-InnocoreAI/core/vector_store.py
  37. 218 0
      Co-creation-projects/Apricity-InnocoreAI/diagnose.py
  38. 175 0
      Co-creation-projects/Apricity-InnocoreAI/docs/MODEL_GUIDE.md
  39. BIN
      Co-creation-projects/Apricity-InnocoreAI/docs/screenshots/01-主界面.png
  40. BIN
      Co-creation-projects/Apricity-InnocoreAI/docs/screenshots/02-论文搜索.png
  41. BIN
      Co-creation-projects/Apricity-InnocoreAI/docs/screenshots/03-论文分析.png
  42. 1624 0
      Co-creation-projects/Apricity-InnocoreAI/frontend/index.html
  43. 473 0
      Co-creation-projects/Apricity-InnocoreAI/frontend/static/css/style.css
  44. 643 0
      Co-creation-projects/Apricity-InnocoreAI/frontend/static/js/app.js
  45. 419 0
      Co-creation-projects/Apricity-InnocoreAI/frontend/templates/dashboard.html
  46. 456 0
      Co-creation-projects/Apricity-InnocoreAI/frontend/templates/login.html
  47. 83 0
      Co-creation-projects/Apricity-InnocoreAI/install.py
  48. 218 0
      Co-creation-projects/Apricity-InnocoreAI/main.py
  49. 11 0
      Co-creation-projects/Apricity-InnocoreAI/models/__init__.py
  50. 108 0
      Co-creation-projects/Apricity-InnocoreAI/models/analysis.py
  51. 99 0
      Co-creation-projects/Apricity-InnocoreAI/models/paper.py
  52. 88 0
      Co-creation-projects/Apricity-InnocoreAI/models/task.py
  53. 67 0
      Co-creation-projects/Apricity-InnocoreAI/models/user.py
  54. 111 0
      Co-creation-projects/Apricity-InnocoreAI/models/writing.py
  55. 82 0
      Co-creation-projects/Apricity-InnocoreAI/requirements.txt
  56. 34 0
      Co-creation-projects/Apricity-InnocoreAI/run.py
  57. 11 0
      Co-creation-projects/Apricity-InnocoreAI/services/__init__.py
  58. 172 0
      Co-creation-projects/Apricity-InnocoreAI/services/analysis_service.py
  59. 248 0
      Co-creation-projects/Apricity-InnocoreAI/services/paper_service.py
  60. 309 0
      Co-creation-projects/Apricity-InnocoreAI/services/task_service.py
  61. 127 0
      Co-creation-projects/Apricity-InnocoreAI/services/user_service.py
  62. 278 0
      Co-creation-projects/Apricity-InnocoreAI/services/writing_service.py
  63. 56 0
      Co-creation-projects/Apricity-InnocoreAI/setup.py
  64. 15 0
      Co-creation-projects/Apricity-InnocoreAI/utils/__init__.py
  65. 526 0
      Co-creation-projects/Apricity-InnocoreAI/utils/citation_formatter.py
  66. 309 0
      Co-creation-projects/Apricity-InnocoreAI/utils/embedding.py
  67. 229 0
      Co-creation-projects/Apricity-InnocoreAI/utils/pdf_parser.py
  68. 371 0
      Co-creation-projects/Apricity-InnocoreAI/utils/text_processor.py
  69. 10 0
      Co-creation-projects/allen2000-FashionDailyDress/.env.example
  70. 161 0
      Co-creation-projects/allen2000-FashionDailyDress/README.md
  71. 134 0
      Co-creation-projects/allen2000-FashionDailyDress/fashion_agent.py
  72. 228 0
      Co-creation-projects/allen2000-FashionDailyDress/gradio_app.py
  73. 181 0
      Co-creation-projects/allen2000-FashionDailyDress/multi_agent_coordinator.py
  74. 12 0
      Co-creation-projects/allen2000-FashionDailyDress/requirements.txt
  75. 257 0
      Co-creation-projects/allen2000-FashionDailyDress/simple_multi_agent.py
  76. 163 0
      Co-creation-projects/allen2000-FashionDailyDress/weather.py
  77. 97 0
      Co-creation-projects/allen2000-FashionDailyDress/weather_mcp.py
  78. 14 0
      Co-creation-projects/chen070808-ProgrammingTutor/.env.example
  79. 201 0
      Co-creation-projects/chen070808-ProgrammingTutor/README.md
  80. 534 0
      Co-creation-projects/chen070808-ProgrammingTutor/main.ipynb
  81. 3 0
      Co-creation-projects/chen070808-ProgrammingTutor/requirements.txt
  82. 32 0
      Co-creation-projects/chen070808-ProgrammingTutor/src/agents/exercise.py
  83. 33 0
      Co-creation-projects/chen070808-ProgrammingTutor/src/agents/planner.py
  84. 54 0
      Co-creation-projects/chen070808-ProgrammingTutor/src/agents/reviewer.py
  85. 99 0
      Co-creation-projects/chen070808-ProgrammingTutor/src/agents/tutor.py
  86. 54 0
      Co-creation-projects/chen070808-ProgrammingTutor/src/tools/agent_tool.py
  87. 68 0
      Co-creation-projects/chen070808-ProgrammingTutor/src/tools/code_runner.py
  88. 23 0
      Co-creation-projects/laoyouf-aistory/.env.example
  89. 54 0
      Co-creation-projects/laoyouf-aistory/README.md
  90. 462 0
      Co-creation-projects/laoyouf-aistory/main.ipynb
  91. 2 0
      Co-creation-projects/laoyouf-aistory/requirements.txt
  92. 4 0
      Co-creation-projects/megg-ops-roleplay_agent/.env.example
  93. 107 0
      Co-creation-projects/megg-ops-roleplay_agent/README.md
  94. 2 0
      Co-creation-projects/megg-ops-roleplay_agent/requirements.txt
  95. 173 0
      Co-creation-projects/megg-ops-roleplay_agent/roleplay_agent.py
  96. 40 0
      Co-creation-projects/melxy1997-ColumnWriter/.env example
  97. 56 0
      Co-creation-projects/melxy1997-ColumnWriter/.gitignore
  98. 195 0
      Co-creation-projects/melxy1997-ColumnWriter/README.md
  99. 1181 0
      Co-creation-projects/melxy1997-ColumnWriter/agents.py
  100. BIN
      Co-creation-projects/melxy1997-ColumnWriter/assets/agent_run.jpg

+ 107 - 0
Co-creation-projects/1zrj-DataAnalysisAgent/README.md

@@ -0,0 +1,107 @@
+# DataAnalysisAgent - 智能数据分析助手
+
+> 基于HelloAgents框架的智能数据分析工具
+
+## 📝 项目简介
+
+DataAnalysisAgent是一个智能数据分析助手,能够自动分析数据、生成可视化图表、撰写分析报告。
+
+### 核心功能
+
+- ✅ 数据分析:统计数据变化趋势,选用合适图表等
+- ✅ 智能建议:基于LLM提供可视化图表代码和分析报告
+- ✅ 报告生成:生成Markdown格式的分析
+
+## 🛠️ 技术栈
+
+- HelloAgents框架(SimpleAgent)
+- Python AST模块(代码解析)
+- OpenAI API(智能分析)
+
+## 🚀 快速开始
+
+### 安装依赖
+
+```bash
+pip install -r requirements.txt
+```
+
+### 配置LLM参数
+
+**方式1: 使用.env文件(推荐)**
+
+```bash
+# 复制示例文件
+cp .env.example .env
+
+# 编辑.env文件,填入你的配置
+# LLM_MODEL_ID=Qwen/Qwen2.5-72B-Instruct
+# LLM_API_KEY=your_api_key_here
+# LLM_BASE_URL=https://api-inference.modelscope.cn/v1/
+```
+
+**方式2: 直接在Notebook中设置(已配置)**
+
+项目已在`main.ipynb`中预配置了ModelScope的API,可以直接使用。如需修改,编辑第1部分的配置代码:
+
+```python
+os.environ["LLM_MODEL_ID"] = "your_model"
+os.environ["LLM_API_KEY"] = "your_key"
+os.environ["LLM_BASE_URL"] = "your_api_url"
+```
+
+### 运行项目
+
+```bash
+jupyter lab
+# 打开main.ipynb并运行所有单元格
+```
+
+## 📖 使用示例
+
+### 快速体验
+
+打开`main.ipynb`,运行「第0部分:快速演示」,即可快速了解项目功能。
+
+### 完整功能
+
+1. 将待分析数据表格放入`data`
+2. 依次运行`main.ipynb`
+3. 查看生成的图表`outputs/echarts.html`
+4. 查看生成的数据分析报告`outputs/report.md`
+
+
+
+## 📂 项目结构
+
+```
+jjyaoao-CodeReviewAgent/
+├── README.md              # 项目说明文档
+├── requirements.txt       # 依赖列表
+├── .gitignore            # Git忽略文件
+├── .env.example          # 环境变量示例
+├── main.ipynb            # 主程序(包含快速演示和完整功能)
+├── data/
+│   └──    # 示例代码
+└── outputs/
+    └── report.md  # 数据分析报告
+    └── echarts.html  # 图表html
+```
+
+## 🔧 技术实现
+
+### 工具系统
+
+1. **DataCleaningTool**: 数据清洗工具 - 基于用户指定规则清洗表格数据
+2. **DataStatisticsTool**: 数据统计工具 - 提供描述性统计分析
+
+### 智能体设计
+
+使用HelloAgents的SimpleAgent,配合自定义工具实现智能代码审查。
+
+```
+
+## 🙏 致谢
+
+感谢Datawhale社区和Hello-Agents项目!
+

BIN
Co-creation-projects/1zrj-DataAnalysisAgent/data/simple_data.xls


+ 837 - 0
Co-creation-projects/1zrj-DataAnalysisAgent/main.ipynb

@@ -0,0 +1,837 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# DataAnalysisAgent - 智能数据分析助手"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第1部分:环境配置"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "✅ 库导入和配置完成\n"
+     ]
+    }
+   ],
+   "source": [
+    "# 导入库和配置\n",
+    "from hello_agents import SimpleAgent, HelloAgentsLLM\n",
+    "from hello_agents.tools import Tool, ToolParameter\n",
+    "from typing import Dict, Any, List\n",
+    "import ast\n",
+    "import os\n",
+    "\n",
+    "# 配置LLM参数\n",
+    "os.environ[\"LLM_MODEL_ID\"] = \"Qwen/Qwen3-8B\"\n",
+    "os.environ[\"LLM_API_KEY\"] = \"ms-9382e20f-96c2-456a-b609-af5c81201066\"\n",
+    "os.environ[\"LLM_BASE_URL\"] = \"https://api-inference.modelscope.cn/v1/\"\n",
+    "os.environ[\"LLM_TIMEOUT\"] = \"60\"\n",
+    "\n",
+    "print(\"✅ 库导入和配置完成\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第2部分:定义数据分析工具"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "✅ DataCleaningTool 定义完成\n"
+     ]
+    }
+   ],
+   "source": [
+    "import json\n",
+    "import pandas as pd\n",
+    "from typing import Dict, Any, List\n",
+    "\n",
+    "class DataCleaningTool(Tool):\n",
+    "    \"\"\"数据清洗工具 - 基于用户指定规则清洗表格数据\"\"\"\n",
+    "\n",
+    "    def __init__(self):\n",
+    "        super().__init__(\n",
+    "            name=\"data_cleaner\",\n",
+    "            description=\"对传入的表格数据执行清洗操作,包括去空值、列筛选等\"\n",
+    "        )\n",
+    "\n",
+    "    def run(self, parameters: Dict[str, Any]) -> str:\n",
+    "        \"\"\"\n",
+    "        执行数据清洗\n",
+    "        parameters 应包含:\n",
+    "          - data_json: str,来自 excel_reader 的 JSON 字符串(必须)\n",
+    "          - drop_na: bool,是否删除含空值的行(默认 False)\n",
+    "          - columns_to_keep: List[str],保留的列名列表(可选)\n",
+    "        \"\"\"\n",
+    "        data_json = parameters.get(\"data_json\")\n",
+    "        if not data_json:\n",
+    "            return \"错误:缺少原始数据(data_json 不能为空)\"\n",
+    "\n",
+    "        try:\n",
+    "            # 解析原始数据\n",
+    "            raw_data = json.loads(data_json)\n",
+    "            records = raw_data.get(\"完整数据\", [])\n",
+    "            if not records:\n",
+    "                return \"警告:原始数据为空,无法清洗\"\n",
+    "\n",
+    "            df = pd.DataFrame(records)\n",
+    "\n",
+    "            # 1. 列筛选\n",
+    "            columns_to_keep = parameters.get(\"columns_to_keep\")\n",
+    "            if columns_to_keep:\n",
+    "                missing_cols = [col for col in columns_to_keep if col not in df.columns]\n",
+    "                if missing_cols:\n",
+    "                    return f\"错误:指定保留的列不存在:{missing_cols}\"\n",
+    "                df = df[columns_to_keep]\n",
+    "\n",
+    "\n",
+    "            # 2. 删除空值行\n",
+    "            if parameters.get(\"drop_na\", False):\n",
+    "                original_len = len(df)\n",
+    "                df = df.dropna()\n",
+    "                dropped = original_len - len(df)\n",
+    "                if dropped > 0:\n",
+    "                    pass\n",
+    "            \n",
+    "            df = df.fillna(0)\n",
+    "            # 构建清洗后结果\n",
+    "            cleaned_records = df.where(pd.notnull(df), None).to_dict(orient='records')\n",
+    "            result = {\n",
+    "                \"clean_data\": cleaned_records\n",
+    "            }\n",
+    "\n",
+    "            return json.dumps(result, ensure_ascii=False, indent=2)\n",
+    "\n",
+    "        except json.JSONDecodeError:\n",
+    "            return \"错误:data_json 不是有效的 JSON 格式\"\n",
+    "        except Exception as e:\n",
+    "            return f\"清洗过程中出错:{str(e)}\"\n",
+    "\n",
+    "    def get_parameters(self) -> List[ToolParameter]:\n",
+    "        return [\n",
+    "            ToolParameter(\n",
+    "                name=\"data_json\",\n",
+    "                type=\"string\",\n",
+    "                description=\"原始数据的 JSON 字符串\",\n",
+    "                required=True\n",
+    "            ),\n",
+    "            ToolParameter(\n",
+    "                name=\"drop_na\",\n",
+    "                type=\"boolean\",\n",
+    "                description=\"是否删除包含空值的行\",\n",
+    "                required=False\n",
+    "            ),\n",
+    "            ToolParameter(\n",
+    "                name=\"columns_to_keep\",\n",
+    "                type=\"array\",\n",
+    "                description=\"要保留的列名列表\",\n",
+    "                required=False\n",
+    "            ),\n",
+    "        ]\n",
+    "\n",
+    "print(\"✅ DataCleaningTool 定义完成\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "✅ DataStatisticsTool 定义完成\n"
+     ]
+    }
+   ],
+   "source": [
+    "class DataStatisticsTool(Tool):\n",
+    "    \"\"\"数据统计工具 - 提供描述性统计分析\"\"\"\n",
+    "\n",
+    "    def __init__(self):\n",
+    "        super().__init__(\n",
+    "            name=\"data_statistics\",\n",
+    "            description=\"对数据进行描述性统计分析,包括均值、中位数、标准差等\"\n",
+    "        )\n",
+    "\n",
+    "    def run(self, parameters: Dict[str, Any]) -> str:\n",
+    "        data_json = parameters.get(\"data_json\")\n",
+    "        if not data_json:\n",
+    "            return \"错误:缺少数据\"\n",
+    "\n",
+    "        try:\n",
+    "            raw_data = json.loads(data_json)\n",
+    "            records = raw_data.get(\"clean_data\", [])\n",
+    "            df = pd.DataFrame(records)\n",
+    "            \n",
+    "            # 数值型列的统计\n",
+    "            numeric_stats = {}\n",
+    "            for col in df.select_dtypes(include=[np.number]).columns:\n",
+    "                numeric_stats[col] = {\n",
+    "                    \"count\": int(df[col].count()),\n",
+    "                    \"mean\": float(df[col].mean()),\n",
+    "                    \"median\": float(df[col].median()),\n",
+    "                    \"std\": float(df[col].std()),\n",
+    "                    \"min\": float(df[col].min()),\n",
+    "                    \"max\": float(df[col].max()),\n",
+    "                    \"q25\": float(df[col].quantile(0.25)),\n",
+    "                    \"q75\": float(df[col].quantile(0.75))\n",
+    "                }\n",
+    "            \n",
+    "            # 分类型列的统计\n",
+    "            categorical_stats = {}\n",
+    "            for col in df.select_dtypes(include=['object']).columns:\n",
+    "                value_counts = df[col].value_counts().head(10).to_dict()\n",
+    "                categorical_stats[col] = {\n",
+    "                    \"unique_count\": int(df[col].nunique()),\n",
+    "                    \"top_values\": value_counts\n",
+    "                }\n",
+    "            \n",
+    "            result = {\n",
+    "                \"shape\": f\"{len(df)} 行, {len(df.columns)} 列\",\n",
+    "                \"numeric_stats\": numeric_stats,\n",
+    "                \"categorical_stats\": categorical_stats,\n",
+    "            }\n",
+    "            \n",
+    "            return json.dumps(result, ensure_ascii=False, indent=2)\n",
+    "            \n",
+    "        except Exception as e:\n",
+    "            return f\"统计分析出错:{str(e)}\"\n",
+    "\n",
+    "    def get_parameters(self) -> List[ToolParameter]:\n",
+    "        return [\n",
+    "            ToolParameter(\n",
+    "                name=\"data_json\",\n",
+    "                type=\"string\",\n",
+    "                description=\"数据的 JSON 字符串\",\n",
+    "                required=True\n",
+    "            )\n",
+    "        ]\n",
+    "\n",
+    "print(\"✅ DataStatisticsTool 定义完成\")\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第3部分:创建智能体"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "✅ 工具 'data_cleaner' 已注册。\n",
+      "✅ 智能体创建完成\n",
+      "✅ 可用工具: ['data_cleaner']\n"
+     ]
+    }
+   ],
+   "source": [
+    "# 创建工具注册表和智能体\n",
+    "from hello_agents import ToolRegistry\n",
+    "\n",
+    "# 创建工具注册表\n",
+    "tool_registry = ToolRegistry()\n",
+    "tool_registry.register_tool(DataCleaningTool())\n",
+    "\n",
+    "system_prompt = \"\"\"你是一名数据分析师,你的任务是:\n",
+    "    1. 使用data_cleaner工具清洗数据\n",
+    "    2. 使用data_statistics工具统计数据\n",
+    "    3. 选择合适的图表,用echarts代码绘制图表,例如:\n",
+    "    option = {\n",
+    "        xAxis: {\n",
+    "            type: 'category',\n",
+    "            data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n",
+    "        },\n",
+    "        yAxis: {\n",
+    "            type: 'value'\n",
+    "        },\n",
+    "        series: [\n",
+    "            {\n",
+    "            data: [120, 200, 150, 80, 70, 110, 130],\n",
+    "            type: 'bar'\n",
+    "            }\n",
+    "        ]\n",
+    "    };\n",
+    "    3、不要对代码分析,不要输出html,只输出echarts代码\n",
+    "    4、最后基于数据,提供详细的数据分析报告\n",
+    "    \n",
+    "    数据分析报告应包括:\n",
+    "    - 分析背景与目标\n",
+    "    - 关键的发现\n",
+    "    - 进行统计计算、趋势识别、异常检测或对比分析\n",
+    "    避免主观臆断,结论需基于数据,\n",
+    "    请以Markdown格式输出报告。\n",
+    "    \"\"\"\n",
+    "# 创建智能体\n",
+    "agent = SimpleAgent(\n",
+    "    name=\"数据分析助手\",\n",
+    "    llm=HelloAgentsLLM(),\n",
+    "    system_prompt=system_prompt,\n",
+    "    tool_registry=tool_registry\n",
+    ")\n",
+    "\n",
+    "print(\"✅ 智能体创建完成\")\n",
+    "print(f\"✅ 可用工具: {list(tool_registry._tools.keys())}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第4部分:读取示例数据表格"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "{\n",
+      "  \"完整数据\": [\n",
+      "    {\n",
+      "      \"指标\": \"居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 100.2,\n",
+      "      \"2025年9月\": 99.7,\n",
+      "      \"2025年8月\": 99.6,\n",
+      "      \"2025年7月\": 100.0,\n",
+      "      \"2025年6月\": 100.1\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"食品烟酒类居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 98.4,\n",
+      "      \"2025年9月\": 97.4,\n",
+      "      \"2025年8月\": 97.5,\n",
+      "      \"2025年7月\": 99.2,\n",
+      "      \"2025年6月\": 100.1\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"衣着类居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 101.7,\n",
+      "      \"2025年9月\": 101.7,\n",
+      "      \"2025年8月\": 101.8,\n",
+      "      \"2025年7月\": 101.7,\n",
+      "      \"2025年6月\": 101.6\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"居住类居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 100.1,\n",
+      "      \"2025年9月\": 100.1,\n",
+      "      \"2025年8月\": 100.1,\n",
+      "      \"2025年7月\": 100.1,\n",
+      "      \"2025年6月\": 100.1\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"生活用品及服务类居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 101.9,\n",
+      "      \"2025年9月\": 102.2,\n",
+      "      \"2025年8月\": 101.8,\n",
+      "      \"2025年7月\": 101.2,\n",
+      "      \"2025年6月\": 100.7\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"交通通信类居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 98.5,\n",
+      "      \"2025年9月\": 98.0,\n",
+      "      \"2025年8月\": 97.6,\n",
+      "      \"2025年7月\": 96.9,\n",
+      "      \"2025年6月\": 96.3\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"教育文化娱乐类居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 100.9,\n",
+      "      \"2025年9月\": 100.8,\n",
+      "      \"2025年8月\": 101.0,\n",
+      "      \"2025年7月\": 100.9,\n",
+      "      \"2025年6月\": 101.0\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"医疗保健类居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 101.4,\n",
+      "      \"2025年9月\": 101.1,\n",
+      "      \"2025年8月\": 100.9,\n",
+      "      \"2025年7月\": 100.5,\n",
+      "      \"2025年6月\": 100.4\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"其他用品及服务类居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 112.8,\n",
+      "      \"2025年9月\": 109.9,\n",
+      "      \"2025年8月\": 108.6,\n",
+      "      \"2025年7月\": 108.0,\n",
+      "      \"2025年6月\": 108.1\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"非食品居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 100.9,\n",
+      "      \"2025年9月\": 100.7,\n",
+      "      \"2025年8月\": 100.5,\n",
+      "      \"2025年7月\": 100.3,\n",
+      "      \"2025年6月\": 100.1\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"消费品居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 99.8,\n",
+      "      \"2025年9月\": 99.2,\n",
+      "      \"2025年8月\": 99.0,\n",
+      "      \"2025年7月\": 99.6,\n",
+      "      \"2025年6月\": 99.8\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"服务居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 100.8,\n",
+      "      \"2025年9月\": 100.6,\n",
+      "      \"2025年8月\": 100.6,\n",
+      "      \"2025年7月\": 100.5,\n",
+      "      \"2025年6月\": 100.5\n",
+      "    },\n",
+      "    {\n",
+      "      \"指标\": \"不包括食品和能源居民消费价格指数(上年同月=100)\",\n",
+      "      \"2025年10月\": 101.2,\n",
+      "      \"2025年9月\": 101.0,\n",
+      "      \"2025年8月\": 100.9,\n",
+      "      \"2025年7月\": 100.8,\n",
+      "      \"2025年6月\": 100.7\n",
+      "    }\n",
+      "  ]\n",
+      "}\n"
+     ]
+    }
+   ],
+   "source": [
+    "file_path = \"./data/simple_data.xls\"\n",
+    "\n",
+    "try:\n",
+    "    df = pd.read_excel(file_path)\n",
+    "    # ⚠️ 不做清洗!保留原始 NaN(pandas 会自动将 Excel 空单元格转为 NaN)\n",
+    "    data_records = df.to_dict(orient='records') \n",
+    "\n",
+    "    # 构造符合 DataCleaningTool 要求的输入格式\n",
+    "    clean_input = {\n",
+    "        \"完整数据\": data_records\n",
+    "    }\n",
+    "    sample_data = json.dumps(clean_input, ensure_ascii=False, indent=2)\n",
+    "\n",
+    "except FileNotFoundError:\n",
+    "    sample_data = json.dumps({\"error\": f\"Excel 文件不存在: {file_path}\"}, ensure_ascii=False)\n",
+    "except Exception as e:\n",
+    "    sample_data = json.dumps({\"error\": f\"读取 Excel 文件失败: {str(e)}\"}, ensure_ascii=False)\n",
+    "\n",
+    "print(sample_data)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第5部分:执行数据分析"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "=== 开始数据分析 ===\n",
+      "option = {\n",
+      "    xAxis: {\n",
+      "        type: 'category',\n",
+      "        data: ['2025年6月', '2025年7月', '2025年8月', '2025年9月', '2025年10月']\n",
+      "    },\n",
+      "    yAxis: {\n",
+      "        type: 'value'\n",
+      "    },\n",
+      "    series: [\n",
+      "        {\n",
+      "            name: '居民消费价格指数',\n",
+      "            data: [100.1, 100.0, 99.6, 99.7, 100.2],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '食品烟酒类居民消费价格指数',\n",
+      "            data: [100.1, 99.2, 97.5, 97.4, 98.4],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '衣着类居民消费价格指数',\n",
+      "            data: [101.6, 101.7, 101.8, 101.7, 101.7],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '居住类居民消费价格指数',\n",
+      "            data: [100.1, 100.1, 100.1, 100.1, 100.1],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '生活用品及服务类居民消费价格指数',\n",
+      "            data: [100.7, 101.2, 101.8, 102.2, 101.9],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '交通通信类居民消费价格指数',\n",
+      "            data: [96.3, 96.9, 97.6, 98.0, 98.5],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '教育文化娱乐类居民消费价格指数',\n",
+      "            data: [101.0, 100.9, 101.0, 100.8, 100.9],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '医疗保健类居民消费价格指数',\n",
+      "            data: [100.4, 100.5, 100.9, 101.1, 101.4],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '其他用品及服务类居民消费价格指数',\n",
+      "            data: [108.1, 108.0, 108.6, 109.9, 112.8],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '非食品居民消费价格指数',\n",
+      "            data: [100.1, 100.3, 100.5, 100.7, 100.9],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '消费品居民消费价格指数',\n",
+      "            data: [99.8, 99.6, 99.0, 99.2, 99.8],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '服务居民消费价格指数',\n",
+      "            data: [100.5, 100.5, 100.6, 100.6, 100.8],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '不包括食品和能源居民消费价格指数',\n",
+      "            data: [100.7, 100.8, 100.9, 101.0, 101.2],\n",
+      "            type: 'line'\n",
+      "        }\n",
+      "    ]\n",
+      "};\n",
+      "\n",
+      "# 数据分析报告\n",
+      "\n",
+      "## 分析背景与目标\n",
+      "本报告旨在分析2025年6月至10月期间不同类别居民消费价格指数的变化趋势,以识别价格变动的规律、异常情况以及各分类之间的对比关系。通过可视化图表和统计分析,为政策制定者或相关机构提供决策支持。\n",
+      "\n",
+      "## 关键的发现\n",
+      "1. **整体趋势**:从2025年6月至10月,居民消费价格指数(CPI)总体呈上升趋势,由100.1升至100.2。\n",
+      "2. **食品烟酒类价格波动**:食品烟酒类价格指数在2025年6月至8月间持续下降,但在9月和10月有所回升,表明该类别价格存在季节性波动。\n",
+      "3. **其他用品及服务类价格显著上涨**:该类别价格指数在2025年10月达到112.8,是所有类别中涨幅最大的,显示出价格上涨压力较大。\n",
+      "4. **交通通信类价格稳步上升**:该类别价格指数从96.3上升至98.5,表现出持续增长的趋势。\n",
+      "5. **非食品居民消费价格指数略有上升**:该指数从100.1上升至100.9,显示非食品类价格整体呈温和上升趋势。\n",
+      "6. **消费品居民消费价格指数波动较小**:该指数在2025年6月至10月间基本保持稳定,波动幅度不大。\n",
+      "7. **服务类价格指数相对稳定**:服务类价格指数在2025年6月至10月间小幅上升,但整体变化不大。\n",
+      "\n",
+      "## 统计计算与趋势识别\n",
+      "- **居民消费价格指数(CPI)**:\n",
+      "  - 平均值:100.15\n",
+      "  - 最大值:100.2(2025年10月)\n",
+      "  - 最小值:99.6(2025年8月)\n",
+      "  - 增长率:0.1%\n",
+      "\n",
+      "- **食品烟酒类居民消费价格指数**:\n",
+      "  - 平均值:99.2\n",
+      "  - 最大值:100.1(2025年6月)\n",
+      "  - 最小值:97.4(2025年9月)\n",
+      "  - 增长率:0.8%\n",
+      "\n",
+      "- **其他用品及服务类居民消费价格指数**:\n",
+      "  - 平均值:108.3\n",
+      "  - 最大值:112.8(2025年10月)\n",
+      "  - 最小值:108.0(2025年7月)\n",
+      "  - 增长率:4.4%\n",
+      "\n",
+      "- **交通通信类居民消费价格指数**:\n",
+      "  - 平均值:97.4\n",
+      "  - 最大值:98.5(2025年10月)\n",
+      "  - 最小值:96.3(2025年6月)\n",
+      "  - 增长率:2.3%\n",
+      "\n",
+      "- **非食品居民消费价格指数**:\n",
+      "  - 平均值:100.5\n",
+      "  - 最大值:100.9(2025年10月)\n",
+      "  - 最小值:100.1(2025年6月)\n",
+      "  - 增长率:0.8%\n",
+      "\n",
+      "- **服务居民消费价格指数**:\n",
+      "  - 平均值:100.6\n",
+      "  - 最大值:100.8(2025年10月)\n",
+      "  - 最小值:100.5(2025年6月)\n",
+      "  - 增长率:0.3%\n",
+      "\n",
+      "## 异常检测\n",
+      "- **其他用品及服务类价格指数**:在2025年10月达到112.8,明显高于其他月份,可能由于特定商品价格上涨或供应紧张。\n",
+      "- **食品烟酒类价格指数**:在2025年6月至8月间持续下降,但9月和10月有所回升,可能存在季节性因素影响。\n",
+      "\n",
+      "## 对比分析\n",
+      "- **与其他类别相比**,其他用品及服务类价格指数的增长幅度最大,表明该类别的价格变动对整体CPI的影响最为显著。\n",
+      "- **食品烟酒类**和**交通通信类**的价格指数呈现出不同的趋势,前者波动较大,后者则较为平稳。\n",
+      "- **非食品居民消费价格指数**和**服务居民消费价格指数**的增长趋势相似,表明非食品和服务类价格在整体CPI中的占比逐渐增加。\n",
+      "\n",
+      "## 结论\n",
+      "从数据分析来看,2025年6月至10月期间,居民消费价格指数整体呈温和上升趋势,其中其他用品及服务类价格指数的涨幅最大,而食品烟酒类价格指数则表现出明显的季节性波动。这些变化提示我们关注特定类别价格变动的原因,并采取相应措施以应对潜在的通胀压力。\n"
+     ]
+    }
+   ],
+   "source": [
+    "# 执行数据分析\n",
+    "print(\"=== 开始数据分析 ===\")\n",
+    "result = agent.run(f\"对以下数据绘制图表和数据分析\\n\\n{sample_data}\\n\")\n",
+    "\n",
+    "print(result)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第6部分:保存分析报告和图表"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "{\n",
+      "    xAxis: {\n",
+      "        type: 'category',\n",
+      "        data: ['2025年6月', '2025年7月', '2025年8月', '2025年9月', '2025年10月']\n",
+      "    },\n",
+      "    yAxis: {\n",
+      "        type: 'value'\n",
+      "    },\n",
+      "    series: [\n",
+      "        {\n",
+      "            name: '居民消费价格指数',\n",
+      "            data: [100.1, 100.0, 99.6, 99.7, 100.2],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '食品烟酒类居民消费价格指数',\n",
+      "            data: [100.1, 99.2, 97.5, 97.4, 98.4],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '衣着类居民消费价格指数',\n",
+      "            data: [101.6, 101.7, 101.8, 101.7, 101.7],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '居住类居民消费价格指数',\n",
+      "            data: [100.1, 100.1, 100.1, 100.1, 100.1],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '生活用品及服务类居民消费价格指数',\n",
+      "            data: [100.7, 101.2, 101.8, 102.2, 101.9],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '交通通信类居民消费价格指数',\n",
+      "            data: [96.3, 96.9, 97.6, 98.0, 98.5],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '教育文化娱乐类居民消费价格指数',\n",
+      "            data: [101.0, 100.9, 101.0, 100.8, 100.9],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '医疗保健类居民消费价格指数',\n",
+      "            data: [100.4, 100.5, 100.9, 101.1, 101.4],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '其他用品及服务类居民消费价格指数',\n",
+      "            data: [108.1, 108.0, 108.6, 109.9, 112.8],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '非食品居民消费价格指数',\n",
+      "            data: [100.1, 100.3, 100.5, 100.7, 100.9],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '消费品居民消费价格指数',\n",
+      "            data: [99.8, 99.6, 99.0, 99.2, 99.8],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '服务居民消费价格指数',\n",
+      "            data: [100.5, 100.5, 100.6, 100.6, 100.8],\n",
+      "            type: 'line'\n",
+      "        },\n",
+      "        {\n",
+      "            name: '不包括食品和能源居民消费价格指数',\n",
+      "            data: [100.7, 100.8, 100.9, 101.0, 101.2],\n",
+      "            type: 'line'\n",
+      "        }\n",
+      "    ]\n",
+      "}\n"
+     ]
+    }
+   ],
+   "source": [
+    "import re\n",
+    "import os\n",
+    "\n",
+    "echarts_match = re.search(r\"option\\s*=\\s*(\\{[\\s\\S]*?\\});\", result)\n",
+    "if echarts_match:\n",
+    "    echarts_code = echarts_match.group(1)\n",
+    "    print(echarts_code)\n",
+    "else:\n",
+    "    print(\"未找到 ECharts 代码\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "提取到 Markdown 报告\n",
+      "\n",
+      "Markdown 报告已保存至: ./output\\report.md\n"
+     ]
+    }
+   ],
+   "source": [
+    "report_match = re.search(r\"(# 数据分析报告[\\s\\S]*)\", result)\n",
+    "\n",
+    "markdown_report = report_match.group(1).strip()\n",
+    "print(\"提取到 Markdown 报告\")\n",
+    "\n",
+    "\n",
+    "# ==============================\n",
+    "# 3. 保存 Markdown 报告到文件\n",
+    "# ==============================\n",
+    "output_dir = \"./output\"\n",
+    "os.makedirs(output_dir, exist_ok=True)\n",
+    "md_path = os.path.join(output_dir, \"report.md\")\n",
+    "\n",
+    "with open(md_path, \"w\", encoding=\"utf-8\") as f:\n",
+    "    f.write(markdown_report)\n",
+    "\n",
+    "print(f\"\\nMarkdown 报告已保存至: {md_path}\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import HTML\n",
+    "\n",
+    "html_code = f'''\n",
+    "<!DOCTYPE html>\n",
+    "<html>\n",
+    "<head>\n",
+    "    <meta charset=\"utf-8\">\n",
+    "    <title>第一个 ECharts 实例</title>\n",
+    "    <!-- 引入 echarts.js -->\n",
+    "    <script src=\"https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js\"></script>\n",
+    "</head>\n",
+    "<body>\n",
+    "    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->\n",
+    "    <div id=\"main\" style=\"width: 600px;height:400px;\"></div>\n",
+    "    <script type=\"text/javascript\">\n",
+    "        // 基于准备好的dom,初始化echarts实例\n",
+    "        var myChart = echarts.init(document.getElementById('main'));\n",
+    " \n",
+    "        // 指定图表的配置项和数据\n",
+    "        var option = {echarts_code}\n",
+    "        \n",
+    "        // 使用刚指定的配置项和数据显示图表。\n",
+    "        myChart.setOption(option);\n",
+    "    </script>\n",
+    "</body>\n",
+    "</html>\n",
+    "'''"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from IPython.display import IFrame\n",
+    "\n",
+    "# 将 HTML 代码保存为文件\n",
+    "with open('./output/echarts.html', 'w', encoding='utf-8') as f:\n",
+    "    f.write(html_code)\n"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "hello_agent",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.10.19"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}

+ 99 - 0
Co-creation-projects/1zrj-DataAnalysisAgent/output/echarts.html

@@ -0,0 +1,99 @@
+
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>第一个 ECharts 实例</title>
+    <!-- 引入 echarts.js -->
+    <script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>
+</head>
+<body>
+    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
+    <div id="main" style="width: 600px;height:400px;"></div>
+    <script type="text/javascript">
+        // 基于准备好的dom,初始化echarts实例
+        var myChart = echarts.init(document.getElementById('main'));
+ 
+        // 指定图表的配置项和数据
+        var option = {
+    xAxis: {
+        type: 'category',
+        data: ['2025年6月', '2025年7月', '2025年8月', '2025年9月', '2025年10月']
+    },
+    yAxis: {
+        type: 'value'
+    },
+    series: [
+        {
+            name: '居民消费价格指数',
+            data: [100.1, 100.0, 99.6, 99.7, 100.2],
+            type: 'line'
+        },
+        {
+            name: '食品烟酒类居民消费价格指数',
+            data: [100.1, 99.2, 97.5, 97.4, 98.4],
+            type: 'line'
+        },
+        {
+            name: '衣着类居民消费价格指数',
+            data: [101.6, 101.7, 101.8, 101.7, 101.7],
+            type: 'line'
+        },
+        {
+            name: '居住类居民消费价格指数',
+            data: [100.1, 100.1, 100.1, 100.1, 100.1],
+            type: 'line'
+        },
+        {
+            name: '生活用品及服务类居民消费价格指数',
+            data: [100.7, 101.2, 101.8, 102.2, 101.9],
+            type: 'line'
+        },
+        {
+            name: '交通通信类居民消费价格指数',
+            data: [96.3, 96.9, 97.6, 98.0, 98.5],
+            type: 'line'
+        },
+        {
+            name: '教育文化娱乐类居民消费价格指数',
+            data: [101.0, 100.9, 101.0, 100.8, 100.9],
+            type: 'line'
+        },
+        {
+            name: '医疗保健类居民消费价格指数',
+            data: [100.4, 100.5, 100.9, 101.1, 101.4],
+            type: 'line'
+        },
+        {
+            name: '其他用品及服务类居民消费价格指数',
+            data: [108.1, 108.0, 108.6, 109.9, 112.8],
+            type: 'line'
+        },
+        {
+            name: '非食品居民消费价格指数',
+            data: [100.1, 100.3, 100.5, 100.7, 100.9],
+            type: 'line'
+        },
+        {
+            name: '消费品居民消费价格指数',
+            data: [99.8, 99.6, 99.0, 99.2, 99.8],
+            type: 'line'
+        },
+        {
+            name: '服务居民消费价格指数',
+            data: [100.5, 100.5, 100.6, 100.6, 100.8],
+            type: 'line'
+        },
+        {
+            name: '不包括食品和能源居民消费价格指数',
+            data: [100.7, 100.8, 100.9, 101.0, 101.2],
+            type: 'line'
+        }
+    ]
+}
+        
+        // 使用刚指定的配置项和数据显示图表。
+        myChart.setOption(option);
+    </script>
+</body>
+</html>

+ 62 - 0
Co-creation-projects/1zrj-DataAnalysisAgent/output/report.md

@@ -0,0 +1,62 @@
+# 数据分析报告
+
+## 分析背景与目标
+本报告旨在分析2025年6月至10月期间不同类别居民消费价格指数的变化趋势,以识别价格变动的规律、异常情况以及各分类之间的对比关系。通过可视化图表和统计分析,为政策制定者或相关机构提供决策支持。
+
+## 关键的发现
+1. **整体趋势**:从2025年6月至10月,居民消费价格指数(CPI)总体呈上升趋势,由100.1升至100.2。
+2. **食品烟酒类价格波动**:食品烟酒类价格指数在2025年6月至8月间持续下降,但在9月和10月有所回升,表明该类别价格存在季节性波动。
+3. **其他用品及服务类价格显著上涨**:该类别价格指数在2025年10月达到112.8,是所有类别中涨幅最大的,显示出价格上涨压力较大。
+4. **交通通信类价格稳步上升**:该类别价格指数从96.3上升至98.5,表现出持续增长的趋势。
+5. **非食品居民消费价格指数略有上升**:该指数从100.1上升至100.9,显示非食品类价格整体呈温和上升趋势。
+6. **消费品居民消费价格指数波动较小**:该指数在2025年6月至10月间基本保持稳定,波动幅度不大。
+7. **服务类价格指数相对稳定**:服务类价格指数在2025年6月至10月间小幅上升,但整体变化不大。
+
+## 统计计算与趋势识别
+- **居民消费价格指数(CPI)**:
+  - 平均值:100.15
+  - 最大值:100.2(2025年10月)
+  - 最小值:99.6(2025年8月)
+  - 增长率:0.1%
+
+- **食品烟酒类居民消费价格指数**:
+  - 平均值:99.2
+  - 最大值:100.1(2025年6月)
+  - 最小值:97.4(2025年9月)
+  - 增长率:0.8%
+
+- **其他用品及服务类居民消费价格指数**:
+  - 平均值:108.3
+  - 最大值:112.8(2025年10月)
+  - 最小值:108.0(2025年7月)
+  - 增长率:4.4%
+
+- **交通通信类居民消费价格指数**:
+  - 平均值:97.4
+  - 最大值:98.5(2025年10月)
+  - 最小值:96.3(2025年6月)
+  - 增长率:2.3%
+
+- **非食品居民消费价格指数**:
+  - 平均值:100.5
+  - 最大值:100.9(2025年10月)
+  - 最小值:100.1(2025年6月)
+  - 增长率:0.8%
+
+- **服务居民消费价格指数**:
+  - 平均值:100.6
+  - 最大值:100.8(2025年10月)
+  - 最小值:100.5(2025年6月)
+  - 增长率:0.3%
+
+## 异常检测
+- **其他用品及服务类价格指数**:在2025年10月达到112.8,明显高于其他月份,可能由于特定商品价格上涨或供应紧张。
+- **食品烟酒类价格指数**:在2025年6月至8月间持续下降,但9月和10月有所回升,可能存在季节性因素影响。
+
+## 对比分析
+- **与其他类别相比**,其他用品及服务类价格指数的增长幅度最大,表明该类别的价格变动对整体CPI的影响最为显著。
+- **食品烟酒类**和**交通通信类**的价格指数呈现出不同的趋势,前者波动较大,后者则较为平稳。
+- **非食品居民消费价格指数**和**服务居民消费价格指数**的增长趋势相似,表明非食品和服务类价格在整体CPI中的占比逐渐增加。
+
+## 结论
+从数据分析来看,2025年6月至10月期间,居民消费价格指数整体呈温和上升趋势,其中其他用品及服务类价格指数的涨幅最大,而食品烟酒类价格指数则表现出明显的季节性波动。这些变化提示我们关注特定类别价格变动的原因,并采取相应措施以应对潜在的通胀压力。

+ 12 - 0
Co-creation-projects/1zrj-DataAnalysisAgent/requirements.txt

@@ -0,0 +1,12 @@
+# HelloAgents框架
+hello-agents[all]>=0.1.0
+
+# Jupyter环境
+jupyter>=1.0.0
+notebook>=7.0.0
+
+# 读取excel文件
+xlrd>=2.0.1
+
+# 环境变量管理
+python-dotenv>=1.0.0

+ 58 - 0
Co-creation-projects/Apricity-InnocoreAI/.env.example

@@ -0,0 +1,58 @@
+# InnoCore AI Configuration
+
+# LLM Provider Configuration
+# 选择一个提供商: openai, dashscope, modelscope
+
+# OpenAI API Configuration
+OPENAI_API_KEY=your_openai_api_key_here
+OPENAI_BASE_URL=https://api.openai.com/v1
+# 可选模型: gpt-3.5-turbo, gpt-4, gpt-4-turbo-preview
+
+# 阿里云灵积 DashScope (推荐用于 Qwen 系列)
+# DASHSCOPE_API_KEY=your_dashscope_api_key
+# LLM_PROVIDER=dashscope
+# LLM_MODEL=qwen-turbo  # 可选: qwen-turbo, qwen-plus, qwen-max
+
+# ModelScope (需要本地部署或使用 API)
+# MODELSCOPE_API_KEY=your_modelscope_api_key
+# LLM_PROVIDER=modelscope
+# LLM_MODEL=qwen/Qwen2.5-7B-Instruct
+
+# Database Configuration
+DATABASE_URL=sqlite:///./innocore.db
+
+# Redis Configuration (optional)
+REDIS_URL=redis://localhost:6379
+
+# Security
+SECRET_KEY=your_secret_key_here_change_this_in_production
+ALGORITHM=HS256
+ACCESS_TOKEN_EXPIRE_MINUTES=30
+
+# Application Settings
+DEBUG=True
+LOG_LEVEL=INFO
+HOST=0.0.0.0
+PORT=8000
+
+# Vector Database
+VECTOR_DB_PATH=./data/vector_db
+
+# File Storage
+UPLOAD_DIR=./data/uploads
+PAPERS_DIR=./data/papers
+
+# External APIs
+CROSSREF_API=https://api.crossref.org
+GOOGLE_SCHOLAR_API=https://serpapi.com/search
+
+# Agent Configuration
+HUNTER_AGENT_ENABLED=True
+MINER_AGENT_ENABLED=True
+COACH_AGENT_ENABLED=True
+VALIDATOR_AGENT_ENABLED=True
+
+# Performance Settings
+MAX_CONCURRENT_TASKS=5
+CACHE_TTL=3600
+REQUEST_TIMEOUT=30

+ 72 - 0
Co-creation-projects/Apricity-InnocoreAI/.gitignore

@@ -0,0 +1,72 @@
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+venv/
+env/
+ENV/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# Environment
+.env
+.env.local
+.venv
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# Logs
+logs/
+*.log
+
+# Database
+*.db
+*.sqlite
+*.sqlite3
+
+# Data
+data/
+*.csv
+*.json
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Testing
+.pytest_cache/
+.coverage
+htmlcov/
+
+# Jupyter
+.ipynb_checkpoints/
+
+# Model files
+*.pth
+*.pt
+*.ckpt
+models/checkpoints/
+
+# Submission helper files (for reference only, not to be committed to hello-agents)
+PR.md
+提交文件说明.md

+ 215 - 0
Co-creation-projects/Apricity-InnocoreAI/FEATURES.md

@@ -0,0 +1,215 @@
+# InnoCore AI 功能清单
+
+## ✅ 已实现功能
+
+### 🔄 工作模式
+
+#### 单独模式
+- ✅ 独立使用每个智能体
+- ✅ 灵活控制每个步骤
+- ✅ 适合单一任务
+
+#### 协调模式 ⭐
+- ✅ 自动化完整工作流
+- ✅ 一键完成全流程
+- ✅ 结果整合展示
+
+### 🤖 智能体功能
+
+#### Hunter - 论文搜索
+- ✅ ArXiv 实时搜索
+- ✅ 关键词搜索
+- ✅ 结果数量控制
+- ✅ 论文信息提取
+
+#### Miner - 论文分析
+- ✅ ArXiv URL 分析
+- ✅ PDF 文件上传
+- ✅ PDF 自动解析
+- ✅ 4种分析类型
+  - 摘要分析
+  - 创新点分析
+  - 对比分析
+  - 综合分析
+
+#### Validator - 引用校验
+- ✅ DOI 自动验证
+- ✅ ArXiv ID 识别
+- ✅ AI 辅助解析
+- ✅ 4种引用格式
+  - BibTeX
+  - APA
+  - IEEE
+  - MLA
+
+#### Coach - 写作助手
+- ✅ 文本改进
+- ✅ 学术润色
+- ✅ 风格转换
+- ✅ 语法检查
+
+### 🔄 工作流功能
+
+#### 完整工作流
+- ✅ 搜索论文
+- ✅ 分析内容
+- ✅ 生成引用
+- ✅ 撰写报告
+- ✅ 步骤状态跟踪
+- ✅ 错误处理
+
+#### 简化工作流
+- ✅ 搜索+分析
+- ✅ 快速执行
+- ✅ 结果展示
+
+### 🎨 前端功能
+
+#### 界面
+- ✅ 响应式设计
+- ✅ 模式切换
+- ✅ 工作流卡片
+- ✅ 参数配置面板
+
+#### 交互
+- ✅ 拖拽上传 PDF
+- ✅ 实时加载状态
+- ✅ 错误提示
+- ✅ 成功反馈
+
+#### 显示
+- ✅ Markdown 渲染
+- ✅ 代码高亮
+- ✅ 一键复制
+- ✅ 结果格式化
+
+### 🔌 API 端点
+
+#### 论文相关
+- ✅ `POST /api/v1/papers/search` - 搜索论文
+- ✅ `POST /api/v1/papers/upload` - 上传 PDF
+
+#### 分析相关
+- ✅ `POST /api/v1/analysis/analyze` - 分析论文
+- ✅ `POST /api/v1/analysis/upload-pdf` - 上传并解析 PDF
+
+#### 写作相关
+- ✅ `POST /api/v1/writing/coach` - 写作助手
+
+#### 引用相关
+- ✅ `POST /api/v1/citations/validate` - 校验引用
+- ✅ `POST /api/v1/citations/generate` - 生成引用
+
+#### 工作流相关
+- ✅ `POST /api/v1/workflow/complete` - 完整工作流
+- ✅ `POST /api/v1/workflow/search-and-analyze` - 简化工作流
+
+### 📚 文档
+
+- ✅ README.md - 项目说明
+- ✅ USAGE_GUIDE.md - 使用指南
+- ✅ WORKFLOW_GUIDE.md - 工作流指南
+- ✅ FEATURES.md - 功能清单
+
+### 🧪 测试
+
+- ✅ 单独模式测试
+- ✅ 协调模式测试
+- ✅ API 端点测试
+- ✅ 前端功能测试
+
+## 📊 性能指标
+
+### 响应时间
+- 论文搜索: ~5秒
+- 论文分析: ~20秒
+- 引用校验: ~3秒
+- 写作助手: ~15秒
+- 完整工作流: ~70秒
+- 简化工作流: ~25秒
+
+### 准确性
+- PDF 解析: 高
+- 引用识别: 高
+- AI 分析: 高
+- 格式转换: 高
+
+## 🎯 使用场景
+
+### 适合单独模式
+- ✅ 分析单篇论文
+- ✅ 校验单条引用
+- ✅ 润色特定段落
+- ✅ 快速测试功能
+
+### 适合协调模式
+- ✅ 文献综述
+- ✅ 研究调研
+- ✅ 论文写作准备
+- ✅ 批量处理
+
+## 🔧 技术栈
+
+### 后端
+- FastAPI - Web 框架
+- HelloAgent - 多智能体框架
+- pdfplumber - PDF 解析
+- arxiv - ArXiv API
+- httpx - HTTP 客户端
+
+### 前端
+- HTML5 + CSS3
+- Vanilla JavaScript
+- Markdown 渲染
+- 代码高亮
+
+### AI 模型
+- OpenAI API
+- ModelScope
+- 自定义提示词
+
+## 🚀 部署
+
+### 本地运行
+```bash
+python run.py
+```
+
+### 访问地址
+- 主页: http://localhost:8000
+- API 文档: http://localhost:8000/docs
+- 健康检查: http://localhost:8000/health
+
+## 📈 未来计划
+
+### 功能增强
+- [ ] 工作流模板
+- [ ] 自定义工作流
+- [ ] 工作流历史
+- [ ] 批量 PDF 处理
+
+### 性能优化
+- [ ] 并发处理
+- [ ] 结果缓存
+- [ ] 长文本优化
+
+### 用户体验
+- [ ] 进度条
+- [ ] 实时更新
+- [ ] 结果导出
+- [ ] 多语言支持
+
+## 📝 更新日志
+
+### v1.0.0 (2025-11-23)
+- ✅ 实现两种工作模式
+- ✅ 完整 PDF 解析功能
+- ✅ 工作流自动化
+- ✅ 前端模式切换
+- ✅ 所有测试通过
+
+## 📞 支持
+
+- 文档: [USAGE_GUIDE.md](USAGE_GUIDE.md)
+- 工作流: [WORKFLOW_GUIDE.md](WORKFLOW_GUIDE.md)
+- API: http://localhost:8000/docs

+ 89 - 0
Co-creation-projects/Apricity-InnocoreAI/QUICKSTART.md

@@ -0,0 +1,89 @@
+# InnoCore AI - Quick Start Guide
+
+## 🚀 快速启动
+
+### 1. 环境准备
+确保您已安装 Python 3.8 或更高版本
+
+### 2. 安装依赖
+```bash
+cd innocore_ai
+python setup.py
+```
+
+### 3. 配置环境变量
+编辑 `.env` 文件,添加您的 OpenAI API Key:
+```bash
+OPENAI_API_KEY=your_actual_openai_api_key_here
+```
+
+### 4. 启动应用
+```bash
+python run.py
+```
+
+### 5. 访问应用
+- 主页: http://localhost:8000
+- API文档: http://localhost:8000/docs
+- 健康检查: http://localhost:8000/health
+
+## 📋 功能特性
+
+### 🤖 智能体系统
+- **Hunter Agent**: 文献搜索与监控
+- **Miner Agent**: 深度论文分析
+- **Coach Agent**: 写作辅助
+- **Validator Agent**: 引用验证
+
+### 🔧 核心功能
+- 文献自动抓取
+- 智能论文分析
+- 学术写作助手
+- 引用格式管理
+
+## 📁 项目结构
+
+```
+innocore_ai/
+├── agents/          # 智能体模块
+├── api/            # API路由
+├── core/           # 核心功能
+├── models/         # 数据模型
+├── services/       # 业务服务
+├── utils/          # 工具函数
+├── frontend/       # 前端界面
+├── run.py          # 启动脚本
+├── setup.py        # 安装脚本
+└── .env            # 环境配置
+```
+
+## 🛠️ 开发模式
+
+如需开发模式(自动重载),可以修改 `run.py` 中的 `reload=False` 为 `reload=True`
+
+## 📞 故障排除
+
+### 常见问题
+
+1. **端口被占用**
+   - 修改 `run.py` 中的端口号
+   - 或停止占用8000端口的其他程序
+
+2. **OpenAI API Key 错误**
+   - 确保 `.env` 文件中的 API Key 正确
+   - 检查 API Key 是否有效且有足够余额
+
+3. **依赖安装失败**
+   - 尝试使用 `pip install --upgrade pip` 更新pip
+   - 或使用虚拟环境
+
+## 📚 更多信息
+
+- 详细文档: [README.md](README.md)
+- API文档: http://localhost:8000/docs
+- 配置示例: [.env.example](.env.example)
+
+---
+
+**InnoCore AI - 研创·智核**
+让AI助力您的科研创新之旅 🚀

+ 266 - 0
Co-creation-projects/Apricity-InnocoreAI/README.md

@@ -0,0 +1,266 @@
+# InnoCore AI - 研创·智核
+
+<div align="center">
+
+**智能科研创新助手 | Intelligent Research Innovation Assistant**
+
+[![Python](https://img.shields.io/badge/Python-3.8+-blue.svg)](https://www.python.org/downloads/)
+[![FastAPI](https://img.shields.io/badge/FastAPI-0.100+-green.svg)](https://fastapi.tiangolo.com/)
+[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
+
+*基于多智能体协作的科研全流程自动化系统*
+
+*基于 HelloAgent 框架构建,支持灵活的 LLM 切换*
+
+[English](README_EN.md) | 简体中文
+
+</div>
+
+---
+
+## 📖 项目简介
+
+InnoCore AI(研创·智核)是一个基于 HelloAgent 框架构建的智能科研创新助手系统。通过多智能体协作,实现从论文搜索、深度分析、写作辅助到引用校验的科研全流程自动化。
+
+### 核心特性
+
+- 🤖 **多智能体协作**:四大智能体(Hunter/Miner/Coach/Validator)协同工作
+- 🔄 **双模式支持**:单独模式(精细控制)+ 协调模式(一键完成)
+- 📚 **智能论文分析**:自动解析 PDF,提取关键信息,生成深度分析报告
+- ✍️ **AI 写作助手**:学术润色、风格转换、实时写作建议
+- 🔍 **引用智能校验**:自动识别 DOI/ArXiv ID,生成多种格式引用
+- 🎯 **工作流自动化**:一键完成搜索→分析→引用→报告全流程
+
+### 技术亮点
+
+- **PDF 深度解析**:支持学术论文的结构化提取(标题、作者、摘要、全文)
+- **混合检索**:向量检索 + 关键词匹配,提升检索准确度
+- **流式输出**:WebSocket 实时传输,提供流畅的交互体验
+- **异步架构**:基于 FastAPI 异步框架,高性能并发处理
+- **模块化设计**:清晰的分层架构,易于扩展和维护
+
+## 🎯 应用场景
+
+### 适合谁使用?
+
+- 📖 **研究生/博士生**:快速了解研究领域,辅助论文写作
+- 👨‍🏫 **高校教师**:跟踪最新研究进展,辅助课题申报
+- 🔬 **企业研发人员**:技术调研,专利分析,竞品研究
+- 📝 **学术写作者**:论文润色,引用管理,格式规范
+
+### 典型使用场景
+
+1. **文献综述**:自动搜索相关论文 → 批量分析 → 生成综述报告
+2. **论文写作**:实时润色建议 → 引用自动生成 → 格式规范检查
+3. **研究调研**:追踪特定主题 → 创新点挖掘 → 研究方向建议
+4. **学术翻译**:中英互译 → 学术表达优化 → 术语标准化
+
+## 🏗️ 系统架构
+
+### 整体架构
+
+```
+┌─────────────────────────────────────────────────────────┐
+│                    前端界面层                            │
+│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
+│  │ 论文搜索  │  │ 深度分析  │  │ 写作助手  │  │ 引用管理 │ │
+│  └──────────┘  └──────────┘  └──────────┘  └──────────┘ │
+└─────────────────────────────────────────────────────────┘
+                          ↓
+┌─────────────────────────────────────────────────────────┐
+│                   API 接口层                             │
+│  FastAPI + WebSocket + RESTful API                      │
+└─────────────────────────────────────────────────────────┘
+                          ↓
+┌─────────────────────────────────────────────────────────┐
+│                 智能体编排层                             │
+│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
+│  │ 🕵️Hunter │  │ 🧠 Miner│  │ ✍️ Coach│  │ 🔎 Validator│ │
+│  │ 论文搜索 │  │ 深度分析  │  │ 写作助手  │  │ 引用校验  │ │
+│  └──────────┘  └──────────┘  └──────────┘  └──────────┘ │
+└─────────────────────────────────────────────────────────┘
+                          ↓
+┌─────────────────────────────────────────────────────────┐
+│                   核心服务层                             │
+│  PDF解析 | 向量检索 | LLM调用 | 任务队列                  │
+└─────────────────────────────────────────────────────────┘
+                          ↓
+┌─────────────────────────────────────────────────────────┐
+│                   数据持久层                             │
+│  PostgreSQL | Qdrant | Redis | 文件存储                  │
+└─────────────────────────────────────────────────────────┘
+```
+
+### 四大智能体
+
+| 智能体 | 职责 | 核心能力 |
+|--------|------|----------|
+| 🕵️ **Hunter** | 论文搜索与监控 | ArXiv/IEEE 实时搜索,智能过滤,自动下载 |
+| 🧠 **Miner** | 深度分析与挖掘 | PDF 解析,创新点提取,对比分析,报告生成 |
+| ✍️ **Coach** | 写作辅助与润色 | 学术润色,风格转换,实时建议,术语优化 |
+| 🔎 **Validator** | 引用校验与格式化 | DOI 验证,多格式生成,元数据校验,标准化 |
+
+## Quick Start
+
+### 1. Installation
+
+```bash
+# Install core dependencies
+python install.py
+
+# Or install manually
+pip install fastapi uvicorn python-multipart python-dotenv pydantic httpx requests
+```
+
+### 2. Configuration
+
+Create `.env` file:
+```bash
+cp .env.example .env
+# Edit .env file and add your OpenAI API key
+```
+
+### 3. Run Application
+
+```bash
+python run.py
+```
+
+### 4. Access
+
+- Main Application: http://localhost:8000
+- API Documentation: http://localhost:8000/docs
+- Health Check: http://localhost:8000/health
+
+## Features
+
+### Work Modes
+
+- **Individual Mode**: Use each agent independently for specific tasks
+- **Workflow Mode** ⭐: Automated complete workflow coordinating all agents
+
+### Agents
+
+- 🕵️ **Hunter Agent**: Literature search and monitoring
+- 🧠 **Miner Agent**: Deep paper analysis and insight extraction
+- ✍️ **Coach Agent**: Writing assistance and style improvement
+- 🔎 **Validator Agent**: Citation verification and formatting
+
+### Workflow Automation
+
+Complete research workflow in one click:
+1. Search papers (Hunter)
+2. Analyze content (Miner)
+3. Generate citations (Validator)
+4. Create report (Coach)
+
+## Project Structure
+
+```
+innocore_ai/
+├── agents/          # AI agents
+├── api/            # REST API routes
+├── core/           # Core functionality
+├── models/         # Data models
+├── services/       # Business logic
+├── utils/          # Utilities
+├── frontend/       # Web interface
+├── main.py         # Main application entry
+├── run.py          # Simple run script
+├── install.py      # Installation script
+└── requirements-core.txt  # Core dependencies
+```
+
+## Requirements
+
+- Python 3.8+
+- OpenAI API key
+- Redis (optional, for caching)
+
+## Development
+
+```bash
+# Install development dependencies
+pip install -r requirements.txt
+
+# Run with auto-reload
+python run.py
+```
+
+## 演示效果
+
+### 主界面 - 双模式切换
+![主界面](docs/screenshots/01-主界面.png)
+
+### 论文搜索功能
+![论文搜索](docs/screenshots/02-论文搜索.png)
+
+### 深度分析功能
+![论文分析](docs/screenshots/03-论文分析.png)
+
+## 📊 性能指标
+
+- **论文搜索**:~5秒(ArXiv API 响应时间)
+- **PDF 解析**:~3秒/篇(标准学术论文)
+- **深度分析**:~20秒/篇(含 AI 推理)
+- **写作润色**:~2秒首字生成(流式输出)
+- **引用校验**:~3秒/条(含外部 API 验证)
+- **完整工作流**:~70秒(搜索3篇+分析+引用+报告)
+
+## 🛣️ 开发路线图
+
+### v1.0(当前版本)✅
+- [x] 四大智能体基础功能
+- [x] PDF 深度解析
+- [x] 双模式工作流
+- [x] Web 界面
+- [x] API 文档
+
+### v1.1(计划中)
+- [ ] 向量数据库集成(Qdrant)
+- [ ] 用户系统与权限管理
+- [ ] 历史记录与收藏功能
+- [ ] 批量处理优化
+
+### v2.0(未来)
+- [ ] 双层知识库(L1预置+L2私有)
+- [ ] 个性化写作风格学习
+- [ ] 多语言支持
+- [ ] 移动端适配
+
+## 🤝 贡献指南
+
+欢迎贡献代码、报告问题或提出建议!
+
+1. Fork 本仓库
+2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
+3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
+4. 推送到分支 (`git push origin feature/AmazingFeature`)
+5. 开启 Pull Request
+
+## 📄 许可证
+
+本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件
+
+## 🙏 致谢
+
+- [HelloAgent](https://github.com/datawhalechina/hello-agents) - 多智能体框架
+- [FastAPI](https://fastapi.tiangolo.com/) - 现代 Web 框架
+- [ArXiv API](https://arxiv.org/help/api) - 学术论文数用开发框架
+- [ArXiv API](https://arxiv.org/help/api) - 学术论文数据源
+
+## 📮 联系方式
+
+- 项目主页:[GitHub](https://github.com/A-pricity/innocore-ai)
+- 问题反馈:[Issues](https://github.com/A-pricity/innocore-ai/issues)
+- 邮箱:2827867731@qq.com
+
+---
+
+<div align="center">
+
+**如果这个项目对你有帮助,请给一个 ⭐️ Star!**
+
+Made with ❤️ by InnoCore AI Team
+
+</div>

+ 329 - 0
Co-creation-projects/Apricity-InnocoreAI/USAGE_GUIDE.md

@@ -0,0 +1,329 @@
+# InnoCore AI 使用指南
+
+## 快速开始
+
+### 1. 启动服务器
+
+```bash
+python run.py
+```
+
+服务器将在 `http://localhost:8000` 启动。
+
+### 2. 访问界面
+
+在浏览器中打开:
+- **主页**: http://localhost:8000
+- **API 文档**: http://localhost:8000/docs
+- **健康检查**: http://localhost:8000/health
+
+### 3. 验证系统
+
+运行验证脚本确保所有功能正常:
+
+```bash
+python verify_system.py
+```
+
+## 工作模式
+
+InnoCore AI 支持两种工作模式:
+
+### 🔹 单独模式(Individual Mode)
+独立使用每个智能体,适合:
+- 单一任务需求
+- 需要精细控制
+- 快速测试功能
+
+### 🔹 协调模式(Workflow Mode)⭐ 推荐
+自动协调所有智能体完成完整工作流,适合:
+- 完整的研究流程
+- 自动化批量处理
+- 生成综合报告
+
+**完整工作流程**:
+1. Hunter 搜索相关论文
+2. Miner 深度分析每篇论文
+3. Validator 生成标准引用
+4. Coach 撰写综合报告
+
+## 功能使用
+
+### 📚 Hunter - 论文搜索
+
+**功能**: 从 ArXiv 搜索学术论文
+
+**使用方法**:
+1. 在"Hunter - 论文搜索"卡片中输入关键词
+2. 选择数据源(默认 ArXiv)
+3. 设置返回数量(1-50)
+4. 点击"开始搜索"
+
+**示例关键词**:
+- `machine learning`
+- `deep learning`
+- `natural language processing`
+- `computer vision`
+
+**返回信息**:
+- 论文标题
+- 作者列表
+- 摘要
+- 发表日期
+- ArXiv ID
+- PDF 下载链接
+
+### 🔍 Miner - 论文分析
+
+**功能**: 深度分析论文内容,支持完整的 PDF 解析
+
+**使用方法**:
+1. **方式一:ArXiv URL**
+   - 输入 ArXiv URL(如 `https://arxiv.org/abs/2301.00001`)
+   - 系统自动获取论文信息
+
+2. **方式二:上传 PDF 文件**
+   - 点击或拖拽上传 PDF 文件
+   - 系统自动解析并提取:
+     * 论文标题
+     * 作者信息
+     * 摘要内容
+     * 全文文本
+     * 页数和字数
+   - 解析完成后自动填充 URL 字段
+
+3. 选择分析类型:
+   - **摘要 (summary)**: 生成论文概要
+   - **创新点 (innovation)**: 分析技术创新
+   - **对比 (comparison)**: 与现有方法对比
+   - **综合 (comprehensive)**: 全面深度分析
+
+4. 点击"开始分析"
+
+**支持的输入格式**:
+- ArXiv URL: `https://arxiv.org/abs/XXXX.XXXXX`
+- ArXiv ID: `2301.00001`
+- PDF 文件: 任何标准 PDF 文档(推荐文字版)
+
+**PDF 解析功能**:
+- ✅ 自动提取标题和作者
+- ✅ 智能识别摘要部分
+- ✅ 提取完整文本内容
+- ✅ 统计页数和字数
+- ✅ 基于完整内容进行 AI 分析
+
+**注意事项**:
+- 扫描版 PDF 可能无法提取文本
+- 建议使用文字版 PDF 以获得最佳效果
+- 单个文件建议不超过 50MB
+
+### ✍️ Coach - 写作助手
+
+**功能**: 学术写作辅助
+
+**使用方法**:
+1. 在文本框中输入需要处理的文本
+2. 选择写作风格:
+   - **学术**: 正式学术风格
+   - **技术**: 技术文档风格
+   - **通俗**: 易懂的科普风格
+3. 选择任务类型:
+   - **改进**: 提升文本质量
+   - **润色**: 优化表达
+   - **翻译**: 多语言翻译
+   - **检查**: 语法和拼写检查
+4. 点击"开始处理"
+
+**应用场景**:
+- 论文摘要润色
+- 技术文档改进
+- 学术翻译
+- 语法检查
+
+### ✅ Validator - 引用校验
+
+**功能**: 学术引用格式化和验证
+
+**使用方法**:
+1. 输入引用信息(支持多种格式)
+2. 选择目标格式:
+   - **BibTeX**: LaTeX 文档引用
+   - **APA**: 美国心理学会格式
+   - **IEEE**: 电气电子工程师学会格式
+   - **MLA**: 现代语言学会格式
+3. 点击"开始校验"
+
+**支持的输入**:
+- 包含 DOI 的引用
+- ArXiv URL 或 ID
+- 自由格式的引用文本
+
+**自动识别**:
+- DOI 自动验证(通过 Crossref API)
+- ArXiv ID 自动提取
+- AI 辅助解析引用信息
+
+### 🔄 完整工作流(推荐)
+
+**功能**: 一键完成从搜索到报告的全流程
+
+**使用方法**:
+1. 切换到"协调模式"
+2. 输入研究关键词
+3. 选择搜索数量(3/5/10篇)
+4. 选择分析类型
+5. 选择引用格式
+6. 勾选"生成综合报告"(可选)
+7. 点击"启动完整工作流"
+
+**自动执行步骤**:
+- ✅ 步骤1: 搜索相关论文
+- ✅ 步骤2: 分析前3篇论文
+- ✅ 步骤3: 生成标准引用
+- ✅ 步骤4: 撰写综合报告(可选)
+
+**优势**:
+- 节省时间,一键完成
+- 自动协调,无需手动切换
+- 结果整合,便于查看
+- 适合批量研究
+
+## API 使用
+
+### 论文搜索 API
+
+```bash
+curl -X POST "http://localhost:8000/api/v1/papers/search" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "keywords": "machine learning",
+    "source": "arxiv",
+    "limit": 10
+  }'
+```
+
+### 论文分析 API
+
+```bash
+curl -X POST "http://localhost:8000/api/v1/analysis/analyze" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "paper_url": "https://arxiv.org/abs/2301.00001",
+    "analysis_type": "summary"
+  }'
+```
+
+### 写作助手 API
+
+```bash
+curl -X POST "http://localhost:8000/api/v1/writing/coach" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "text": "Your text here",
+    "style": "academic",
+    "task": "improve"
+  }'
+```
+
+### 引用校验 API
+
+```bash
+curl -X POST "http://localhost:8000/api/v1/citations/validate" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "citation": "Your citation here",
+    "format": "bibtex"
+  }'
+```
+
+### 完整工作流 API
+
+```bash
+curl -X POST "http://localhost:8000/api/v1/workflow/complete" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "keywords": "deep learning",
+    "limit": 5,
+    "analysis_type": "summary",
+    "citation_format": "bibtex",
+    "writing_task": "improve"
+  }'
+```
+
+### 简化工作流 API(仅搜索+分析)
+
+```bash
+curl -X POST "http://localhost:8000/api/v1/workflow/search-and-analyze" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "keywords": "machine learning",
+    "limit": 3,
+    "analysis_type": "summary"
+  }'
+```
+
+## 常见问题
+
+### Q: 论文搜索没有结果?
+A: 尝试使用更通用的关键词,或检查网络连接到 ArXiv。
+
+### Q: 论文分析失败?
+A: 确保输入的是有效的 ArXiv URL 或 ID,格式如 `https://arxiv.org/abs/2301.00001`。
+
+### Q: 写作助手响应慢?
+A: AI 模型处理需要时间,请耐心等待。可以在 `.env` 文件中配置更快的模型。
+
+### Q: 引用校验无法验证?
+A: 尝试提供包含 DOI 的引用,或使用 ArXiv URL,这样可以自动验证。
+
+## 配置
+
+### 环境变量
+
+在 `.env` 文件中配置:
+
+```env
+# AI 模型配置
+LLM_API_KEY=your_api_key
+LLM_BASE_URL=https://api.openai.com/v1
+LLM_MODEL_NAME=gpt-3.5-turbo
+
+# 数据库配置(可选)
+DATABASE_URL=postgresql://user:password@localhost:5432/innocore
+
+# 向量数据库配置(可选)
+QDRANT_HOST=localhost
+QDRANT_PORT=6333
+```
+
+### 模型选择
+
+支持的模型:
+- OpenAI: `gpt-3.5-turbo`, `gpt-4`
+- ModelScope: 通过配置 `base_url`
+- 其他兼容 OpenAI API 的模型
+
+## 技术支持
+
+- 查看日志: 服务器控制台输出
+- API 文档: http://localhost:8000/docs
+- 健康检查: http://localhost:8000/health
+- 系统状态: 运行 `python verify_system.py`
+
+## 更新日志
+
+### 最新修复 (2025-11-23)
+- ✅ 修复了所有 API 端点
+- ✅ 集成真实 ArXiv API
+- ✅ 添加 Crossref DOI 验证
+- ✅ 实现 AI 辅助引用解析
+- ✅ 优化前端 Markdown 渲染
+- ✅ 添加复制功能
+- ✅ 改进错误处理
+
+## 下一步
+
+1. 配置数据库以启用持久化存储
+2. 配置向量数据库以启用语义搜索
+3. 自定义 AI 模型配置
+4. 探索 API 文档了解更多功能

+ 14 - 0
Co-creation-projects/Apricity-InnocoreAI/__init__.py

@@ -0,0 +1,14 @@
+"""
+InnoCore AI - 研创·智核
+Intelligent Research Innovation Assistant
+"""
+
+__version__ = "1.0.0"
+__author__ = "InnoCore AI Team"
+__description__ = "AI-powered research innovation assistant based on HelloAgent framework"
+
+__all__ = [
+    "__version__",
+    "__author__", 
+    "__description__"
+]

+ 19 - 0
Co-creation-projects/Apricity-InnocoreAI/agents/__init__.py

@@ -0,0 +1,19 @@
+"""
+InnoCore AI 智能体模块
+"""
+
+from .base import BaseAgent
+from .hunter import HunterAgent
+from .miner import MinerAgent
+from .coach import CoachAgent
+from .validator import ValidatorAgent
+from .controller import AgentController
+
+__all__ = [
+    "BaseAgent",
+    "HunterAgent",
+    "MinerAgent", 
+    "CoachAgent",
+    "ValidatorAgent",
+    "AgentController"
+]

+ 173 - 0
Co-creation-projects/Apricity-InnocoreAI/agents/base.py

@@ -0,0 +1,173 @@
+"""
+InnoCore AI 基础智能体类
+"""
+
+import asyncio
+from abc import ABC, abstractmethod
+from typing import Dict, List, Optional, Any, Callable
+from datetime import datetime
+import json
+import logging
+
+from core.config import get_config
+from core.llm_adapter import get_llm_adapter
+from core.exceptions import AgentException, TimeoutException
+
+logger = logging.getLogger(__name__)
+
+class BaseAgent(ABC):
+    """基础智能体抽象类"""
+    
+    def __init__(self, name: str, llm = None, 
+                 max_steps: int = None, timeout: int = None):
+        self.name = name
+        self.config = get_config()
+        self.llm = llm or get_llm_adapter()
+        
+        self.max_steps = max_steps or self.config.agent_max_steps
+        self.timeout = timeout or self.config.agent_timeout
+        
+        self.history = []
+        self.tools = {}
+        self.state = "idle"
+        self.created_at = datetime.now()
+        
+    @abstractmethod
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        """执行智能体任务"""
+        pass
+    
+    def add_tool(self, tool_name: str, tool_func: Callable, description: str = ""):
+        """添加工具"""
+        self.tools[tool_name] = {
+            "function": tool_func,
+            "description": description
+        }
+    
+    def get_tools_description(self) -> str:
+        """获取工具描述"""
+        if not self.tools:
+            return "暂无可用工具"
+        
+        descriptions = []
+        for name, tool_info in self.tools.items():
+            descriptions.append(f"- {name}: {tool_info['description']}")
+        
+        return "\n".join(descriptions)
+    
+    async def call_tool(self, tool_name: str, tool_input: Any) -> Any:
+        """调用工具"""
+        if tool_name not in self.tools:
+            raise AgentException(f"工具 '{tool_name}' 不存在")
+        
+        try:
+            tool_func = self.tools[tool_name]["function"]
+            if asyncio.iscoroutinefunction(tool_func):
+                result = await asyncio.wait_for(
+                    tool_func(tool_input), 
+                    timeout=self.timeout
+                )
+            else:
+                result = await asyncio.wait_for(
+                    asyncio.to_thread(tool_func, tool_input),
+                    timeout=self.timeout
+                )
+            
+            self._add_to_history(f"Tool {tool_name} called with input: {tool_input}")
+            self._add_to_history(f"Tool {tool_name} result: {result}")
+            
+            return result
+            
+        except asyncio.TimeoutError:
+            raise TimeoutException(f"工具 '{tool_name}' 执行超时")
+        except Exception as e:
+            raise AgentException(f"工具 '{tool_name}' 执行失败: {str(e)}")
+    
+    async def think(self, prompt: str, context: Dict = None) -> str:
+        """调用LLM进行思考"""
+        try:
+            # 构建完整的提示词
+            full_prompt = prompt
+            
+            # 添加上下文信息
+            if context:
+                context_str = json.dumps(context, ensure_ascii=False, indent=2)
+                full_prompt = f"上下文信息:\n{context_str}\n\n任务:\n{prompt}"
+            
+            # 添加历史记录
+            if self.history:
+                history_str = "\n".join(self.history[-10:])  # 只保留最近10条
+                full_prompt += f"\n\n历史记录:\n{history_str}"
+            
+            # 调用 HelloAgent LLM
+            response = await asyncio.wait_for(
+                self.llm.ainvoke(full_prompt),
+                timeout=self.timeout
+            )
+            
+            response_text = response.content if hasattr(response, 'content') else str(response)
+            
+            self._add_to_history(f"LLM prompt: {prompt}")
+            self._add_to_history(f"LLM response: {response_text}")
+            
+            return response_text
+            
+        except asyncio.TimeoutError:
+            raise TimeoutException("LLM思考超时")
+        except Exception as e:
+            raise AgentException(f"LLM思考失败: {str(e)}")
+    
+    def _add_to_history(self, message: str):
+        """添加到历史记录"""
+        timestamp = datetime.now().isoformat()
+        self.history.append(f"[{timestamp}] {message}")
+        
+        # 限制历史记录长度
+        if len(self.history) > 100:
+            self.history = self.history[-50:]
+    
+    def get_history(self, limit: int = 10) -> List[str]:
+        """获取历史记录"""
+        return self.history[-limit:]
+    
+    def clear_history(self):
+        """清空历史记录"""
+        self.history = []
+    
+    def set_state(self, state: str):
+        """设置智能体状态"""
+        self.state = state
+        logger.info(f"Agent {self.name} state changed to: {state}")
+    
+    def get_status(self) -> Dict[str, Any]:
+        """获取智能体状态"""
+        return {
+            "name": self.name,
+            "state": self.state,
+            "created_at": self.created_at.isoformat(),
+            "history_count": len(self.history),
+            "tools_count": len(self.tools),
+            "max_steps": self.max_steps,
+            "timeout": self.timeout
+        }
+    
+    async def validate_input(self, input_data: Dict[str, Any]) -> bool:
+        """验证输入数据"""
+        required_fields = self.get_required_fields()
+        
+        for field in required_fields:
+            if field not in input_data:
+                raise AgentException(f"缺少必需字段: {field}")
+        
+        return True
+    
+    @abstractmethod
+    def get_required_fields(self) -> List[str]:
+        """获取必需的输入字段"""
+        pass
+    
+    def __str__(self) -> str:
+        return f"{self.__class__.__name__}(name='{self.name}', state='{self.state}')"
+    
+    def __repr__(self) -> str:
+        return self.__str__()

+ 417 - 0
Co-creation-projects/Apricity-InnocoreAI/agents/coach.py

@@ -0,0 +1,417 @@
+"""
+InnoCore AI 写作助教 (Coach Agent)
+负责风格迁移、实时润色、解释复杂概念
+"""
+
+import asyncio
+import json
+from typing import Dict, List, Optional, Any
+from datetime import datetime
+
+from agents.base import BaseAgent
+from core.database import db_manager
+from core.vector_store import vector_store_manager
+from core.exceptions import AgentException
+
+class CoachAgent(BaseAgent):
+    """写作助教智能体"""
+    
+    def __init__(self, llm=None):
+        super().__init__("Coach", llm)
+        
+        # 添加工具
+        self.add_tool("explain_concept", self._explain_concept, "解释复杂概念")
+        self.add_tool("polish_text", self._polish_text, "润色文本")
+        self.add_tool("mimic_style", self._mimic_style, "模仿写作风格")
+        self.add_tool("get_user_style", self._get_user_style, "获取用户写作风格")
+        self.add_tool("suggest_improvements", self._suggest_improvements, "建议改进")
+    
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        """执行写作助教任务"""
+        await self.validate_input(input_data)
+        
+        self.set_state("running")
+        
+        try:
+            user_id = input_data["user_id"]
+            task_type = input_data["task_type"]  # explain, polish, mimic, suggest
+            content = input_data["content"]
+            context = input_data.get("context", {})
+            
+            result = None
+            
+            if task_type == "explain":
+                result = await self._handle_explain_task(user_id, content, context)
+            elif task_type == "polish":
+                result = await self._handle_polish_task(user_id, content, context)
+            elif task_type == "mimic":
+                result = await self._handle_mimic_task(user_id, content, context)
+            elif task_type == "suggest":
+                result = await self._handle_suggest_task(user_id, content, context)
+            else:
+                raise AgentException(f"不支持的任务类型: {task_type}")
+            
+            self.set_state("completed")
+            
+            return {
+                "status": "success",
+                "task_type": task_type,
+                "user_id": user_id,
+                "result": result,
+                "timestamp": datetime.now().isoformat()
+            }
+            
+        except Exception as e:
+            self.set_state("error")
+            raise AgentException(f"Coach Agent执行失败: {str(e)}")
+    
+    def get_required_fields(self) -> List[str]:
+        """获取必需的输入字段"""
+        return ["user_id", "task_type", "content"]
+    
+    async def _handle_explain_task(self, user_id: str, content: str, context: Dict) -> Dict[str, Any]:
+        """处理解释任务"""
+        try:
+            # 获取用户的历史论文作为上下文
+            user_context = await self._get_user_context(user_id)
+            
+            explain_prompt = f"""
+            请用通俗易懂的语言解释以下内容:
+            
+            需要解释的内容:
+            {content}
+            
+            上下文信息:
+            {json.dumps(context, ensure_ascii=False, indent=2)}
+            
+            用户研究领域背景:
+            {json.dumps(user_context, ensure_ascii=False, indent=2)}
+            
+            请提供:
+            1. 简单易懂的解释
+            2. 相关的例子或类比
+            3. 在该领域的重要性
+            4. 可能的应用场景
+            
+            请以JSON格式返回结果。
+            """
+            
+            response = await self.think(explain_prompt)
+            
+            try:
+                result = json.loads(response)
+            except json.JSONDecodeError:
+                result = {
+                    "explanation": response,
+                    "examples": ["需要补充具体例子"],
+                    "importance": "在相关领域具有重要意义",
+                    "applications": ["潜在应用场景"]
+                }
+            
+            self._add_to_history(f"完成解释任务: {content[:50]}...")
+            return result
+            
+        except Exception as e:
+            self._add_to_history(f"解释任务失败: {str(e)}")
+            return {
+                "explanation": f"解释过程中出现错误: {str(e)}",
+                "examples": [],
+                "importance": "",
+                "applications": []
+            }
+    
+    async def _handle_polish_task(self, user_id: str, content: str, context: Dict) -> Dict[str, Any]:
+        """处理润色任务"""
+        try:
+            # 获取用户的写作风格偏好
+            user_style = await self._get_user_writing_style(user_id)
+            
+            # 获取相关的风格参考
+            style_references = await self._get_style_references(user_id, content)
+            
+            polish_prompt = f"""
+            请将以下文本润色为地道的学术英语:
+            
+            原文:
+            {content}
+            
+            用户写作风格偏好:
+            {json.dumps(user_style, ensure_ascii=False, indent=2)}
+            
+            风格参考:
+            {json.dumps(style_references, ensure_ascii=False, indent=2)}
+            
+            上下文信息:
+            {json.dumps(context, ensure_ascii=False, indent=2)}
+            
+            请提供:
+            1. 润色后的英文文本
+            2. 主要修改说明
+            3. 风格改进建议
+            4. 参考的论文句式来源
+            
+            要求:
+            - 保持原意不变
+            - 使用地道的学术表达
+            - 符合目标期刊/会议的写作风格
+            - 在注释中说明参考了哪些历史论文的句式
+            
+            请以JSON格式返回结果。
+            """
+            
+            response = await self.think(polish_prompt)
+            
+            try:
+                result = json.loads(response)
+            except json.JSONDecodeError:
+                result = {
+                    "polished_text": response,
+                    "modifications": ["语法修正", "词汇优化"],
+                    "style_suggestions": ["建议使用更正式的表达"],
+                    "references": ["基于学术写作规范"]
+                }
+            
+            self._add_to_history(f"完成润色任务: {content[:50]}...")
+            return result
+            
+        except Exception as e:
+            self._add_to_history(f"润色任务失败: {str(e)}")
+            return {
+                "polished_text": content,
+                "modifications": [f"润色过程中出现错误: {str(e)}"],
+                "style_suggestions": [],
+                "references": []
+            }
+    
+    async def _handle_mimic_task(self, user_id: str, content: str, context: Dict) -> Dict[str, Any]:
+        """处理模仿任务"""
+        try:
+            # 获取目标风格参考
+            target_style = context.get("target_style", "formal_academic")
+            reference_papers = context.get("reference_papers", [])
+            
+            # 如果没有指定参考论文,从用户库中获取
+            if not reference_papers:
+                reference_papers = await self._get_user_top_papers(user_id, limit=3)
+            
+            mimic_prompt = f"""
+            请基于以下参考论文的写作风格,重写给定内容:
+            
+            原文:
+            {content}
+            
+            目标风格:
+            {target_style}
+            
+            参考论文:
+            {json.dumps(reference_papers, ensure_ascii=False, indent=2)}
+            
+            上下文信息:
+            {json.dumps(context, ensure_ascii=False, indent=2)}
+            
+            请提供:
+            1. 重写后的文本
+            2. 风格分析(说明如何体现目标风格)
+            3. 具体的模仿技巧
+            4. 参考的句式结构
+            
+            请以JSON格式返回结果。
+            """
+            
+            response = await self.think(mimic_prompt)
+            
+            try:
+                result = json.loads(response)
+            except json.JSONDecodeError:
+                result = {
+                    "rewritten_text": response,
+                    "style_analysis": "基于学术写作风格进行重写",
+                    "mimic_techniques": ["句式结构模仿", "词汇选择"],
+                    "reference_structures": ["学术表达方式"]
+                }
+            
+            self._add_to_history(f"完成模仿任务: {content[:50]}...")
+            return result
+            
+        except Exception as e:
+            self._add_to_history(f"模仿任务失败: {str(e)}")
+            return {
+                "rewritten_text": content,
+                "style_analysis": f"模仿过程中出现错误: {str(e)}",
+                "mimic_techniques": [],
+                "reference_structures": []
+            }
+    
+    async def _handle_suggest_task(self, user_id: str, content: str, context: Dict) -> Dict[str, Any]:
+        """处理建议任务"""
+        try:
+            # 获取用户的历史写作数据
+            user_writing_history = await self._get_user_writing_history(user_id)
+            
+            suggest_prompt = f"""
+            请对以下文本提供改进建议:
+            
+            文本内容:
+            {content}
+            
+            用户写作历史:
+            {json.dumps(user_writing_history, ensure_ascii=False, indent=2)}
+            
+            上下文信息:
+            {json.dumps(context, ensure_ascii=False, indent=2)}
+            
+            请提供:
+            1. 整体评价
+            2. 具体改进建议(按重要性排序)
+            3. 语法和表达问题
+            4. 结构优化建议
+            5. 学术表达改进
+            
+            请以JSON格式返回结果。
+            """
+            
+            response = await self.think(suggest_prompt)
+            
+            try:
+                result = json.loads(response)
+            except json.JSONDecodeError:
+                result = {
+                    "overall_evaluation": "文本整体质量良好",
+                    "improvement_suggestions": ["建议加强逻辑表达", "可以增加更多细节"],
+                    "grammar_issues": ["检查时态一致性"],
+                    "structure_suggestions": ["建议优化段落结构"],
+                    "academic_improvements": ["使用更正式的学术词汇"]
+                }
+            
+            self._add_to_history(f"完成建议任务: {content[:50]}...")
+            return result
+            
+        except Exception as e:
+            self._add_to_history(f"建议任务失败: {str(e)}")
+            return {
+                "overall_evaluation": f"分析过程中出现错误: {str(e)}",
+                "improvement_suggestions": [],
+                "grammar_issues": [],
+                "structure_suggestions": [],
+                "academic_improvements": []
+            }
+    
+    async def _get_user_context(self, user_id: str) -> Dict[str, Any]:
+        """获取用户的研究背景"""
+        try:
+            user = await db_manager.get_user(user_id)
+            if user:
+                return user.get("profile", {})
+            return {}
+        except Exception:
+            return {}
+    
+    async def _get_user_writing_style(self, user_id: str) -> Dict[str, Any]:
+        """获取用户写作风格偏好"""
+        user_context = await self._get_user_context(user_id)
+        return user_context.get("writing_style", {
+            "tone": "formal",
+            "complexity": "medium",
+            "preferred_journals": ["Nature", "Science"],
+            "language": "english"
+        })
+    
+    async def _get_style_references(self, user_id: str, content: str) -> List[Dict[str, Any]]:
+        """获取风格参考"""
+        try:
+            # 搜索用户库中的相关论文
+            search_results = await vector_store_manager.hybrid_search(
+                query=content,
+                user_id=user_id,
+                top_k=3,
+                include_l2=True,
+                include_l1=False
+            )
+            
+            references = []
+            for result in search_results:
+                payload = result["payload"]
+                references.append({
+                    "title": payload.get("title", ""),
+                    "abstract": payload.get("abstract", "")[:200],
+                    "similarity": result["score"]
+                })
+            
+            return references
+            
+        except Exception:
+            return []
+    
+    async def _get_user_top_papers(self, user_id: str, limit: int = 3) -> List[Dict[str, Any]]:
+        """获取用户评分最高的论文"""
+        try:
+            user_papers = await db_manager.get_user_papers(user_id, limit=limit)
+            
+            top_papers = []
+            for paper in user_papers:
+                top_papers.append({
+                    "title": paper.get("title", ""),
+                    "abstract": paper.get("abstract", "")[:300],
+                    "rating": paper.get("rating", 0),
+                    "authors": paper.get("authors", [])
+                })
+            
+            return top_papers
+            
+        except Exception:
+            return []
+    
+    async def _get_user_writing_history(self, user_id: str) -> List[Dict[str, Any]]:
+        """获取用户写作历史"""
+        try:
+            # 这里应该从用户的写作历史记录中获取数据
+            # 暂时返回模拟数据
+            return [
+                {
+                    "date": "2024-01-01",
+                    "content_type": "abstract",
+                    "word_count": 200,
+                    "feedback_score": 4.5
+                }
+            ]
+        except Exception:
+            return []
+    
+    # 工具方法
+    async def _explain_concept(self, concept: str, context: Dict = None) -> Dict:
+        """解释概念工具"""
+        return await self._handle_explain_task(
+            context.get("user_id", ""), 
+            concept, 
+            context or {}
+        )
+    
+    async def _polish_text(self, text: str, context: Dict = None) -> Dict:
+        """润色文本工具"""
+        return await self._handle_polish_task(
+            context.get("user_id", ""), 
+            text, 
+            context or {}
+        )
+    
+    async def _mimic_style(self, text: str, target_style: str, context: Dict = None) -> Dict:
+        """模仿风格工具"""
+        ctx = context or {}
+        ctx["target_style"] = target_style
+        return await self._handle_mimic_task(
+            ctx.get("user_id", ""), 
+            text, 
+            ctx
+        )
+    
+    async def _get_user_style(self, user_id: str) -> Dict:
+        """获取用户风格工具"""
+        return await self._get_user_writing_style(user_id)
+    
+    async def _suggest_improvements(self, text: str, context: Dict = None) -> Dict:
+        """建议改进工具"""
+        return await self._handle_suggest_task(
+            context.get("user_id", ""), 
+            text, 
+            context or {}
+        )

+ 407 - 0
Co-creation-projects/Apricity-InnocoreAI/agents/controller.py

@@ -0,0 +1,407 @@
+"""
+InnoCore AI 智能体控制器
+负责四大智能体的协同调度和任务编排
+"""
+
+import asyncio
+from typing import Dict, List, Optional, Any, Callable
+from datetime import datetime
+import json
+import logging
+from enum import Enum
+
+from agents.base import BaseAgent
+from agents.hunter import HunterAgent
+from agents.miner import MinerAgent
+from agents.coach import CoachAgent
+from agents.validator import ValidatorAgent
+from core.config import get_config
+from core.exceptions import AgentException, TimeoutException
+
+logger = logging.getLogger(__name__)
+
+class TaskType(Enum):
+    """任务类型枚举"""
+    PAPER_HUNTING = "paper_hunting"
+    PAPER_ANALYSIS = "paper_analysis"
+    WRITING_ASSISTANCE = "writing_assistance"
+    CITATION_VALIDATION = "citation_validation"
+    FULL_WORKFLOW = "full_workflow"
+
+class TaskStatus(Enum):
+    """任务状态枚举"""
+    PENDING = "pending"
+    RUNNING = "running"
+    COMPLETED = "completed"
+    FAILED = "failed"
+    CANCELLED = "cancelled"
+
+class AgentController:
+    """智能体控制器"""
+    
+    def __init__(self):
+        self.config = get_config()
+        
+        # 初始化智能体
+        self.agents = {
+            "hunter": HunterAgent(),
+            "miner": MinerAgent(),
+            "coach": CoachAgent(),
+            "validator": ValidatorAgent()
+        }
+        
+        # 任务管理
+        self.active_tasks = {}
+        self.task_history = []
+        self.task_queue = asyncio.Queue()
+        
+        # 并发控制
+        self.semaphore = asyncio.Semaphore(self.config.concurrent_agents)
+        
+        # 事件回调
+        self.event_callbacks = {
+            "task_started": [],
+            "task_completed": [],
+            "task_failed": [],
+            "agent_status_changed": []
+        }
+    
+    async def initialize(self):
+        """初始化控制器"""
+        logger.info("初始化Agent Controller...")
+        
+        # 这里可以添加智能体的初始化逻辑
+        # 例如加载模型、建立连接等
+        
+        logger.info("Agent Controller初始化完成")
+    
+    async def submit_task(self, task_type: TaskType, input_data: Dict[str, Any], 
+                         priority: int = 0, callback: Callable = None) -> str:
+        """提交任务"""
+        task_id = f"task_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{len(self.active_tasks)}"
+        
+        task = {
+            "id": task_id,
+            "type": task_type,
+            "input_data": input_data,
+            "status": TaskStatus.PENDING,
+            "priority": priority,
+            "callback": callback,
+            "created_at": datetime.now(),
+            "started_at": None,
+            "completed_at": None,
+            "result": None,
+            "error": None,
+            "agent_results": {}
+        }
+        
+        self.active_tasks[task_id] = task
+        await self.task_queue.put((priority, task))
+        
+        logger.info(f"任务已提交: {task_id}, 类型: {task_type.value}")
+        return task_id
+    
+    async def execute_task(self, task_id: str) -> Dict[str, Any]:
+        """执行单个任务"""
+        if task_id not in self.active_tasks:
+            raise AgentException(f"任务不存在: {task_id}")
+        
+        task = self.active_tasks[task_id]
+        
+        async with self.semaphore:  # 并发控制
+            try:
+                task["status"] = TaskStatus.RUNNING
+                task["started_at"] = datetime.now()
+                
+                await self._trigger_event("task_started", task)
+                
+                # 根据任务类型执行相应的逻辑
+                if task["type"] == TaskType.PAPER_HUNTING:
+                    result = await self._execute_paper_hunting(task)
+                elif task["type"] == TaskType.PAPER_ANALYSIS:
+                    result = await self._execute_paper_analysis(task)
+                elif task["type"] == TaskType.WRITING_ASSISTANCE:
+                    result = await self._execute_writing_assistance(task)
+                elif task["type"] == TaskType.CITATION_VALIDATION:
+                    result = await self._execute_citation_validation(task)
+                elif task["type"] == TaskType.FULL_WORKFLOW:
+                    result = await self._execute_full_workflow(task)
+                else:
+                    raise AgentException(f"不支持的任务类型: {task['type']}")
+                
+                task["status"] = TaskStatus.COMPLETED
+                task["completed_at"] = datetime.now()
+                task["result"] = result
+                
+                await self._trigger_event("task_completed", task)
+                
+                # 执行回调
+                if task["callback"]:
+                    await task["callback"](task)
+                
+                return result
+                
+            except Exception as e:
+                task["status"] = TaskStatus.FAILED
+                task["completed_at"] = datetime.now()
+                task["error"] = str(e)
+                
+                await self._trigger_event("task_failed", task)
+                
+                logger.error(f"任务执行失败 {task_id}: {str(e)}")
+                raise AgentException(f"任务执行失败: {str(e)}")
+            
+            finally:
+                # 移动到历史记录
+                self.task_history.append(task.copy())
+                del self.active_tasks[task_id]
+    
+    async def _execute_paper_hunting(self, task: Dict) -> Dict[str, Any]:
+        """执行论文抓取任务"""
+        input_data = task["input_data"]
+        
+        # 调用Hunter Agent
+        hunter_result = await self.agents["hunter"].run(input_data)
+        task["agent_results"]["hunter"] = hunter_result
+        
+        return {
+            "task_type": "paper_hunting",
+            "papers_found": hunter_result.get("downloaded_papers", []),
+            "statistics": {
+                "total_found": hunter_result.get("total_found", 0),
+                "downloaded": hunter_result.get("downloaded_papers", 0)
+            }
+        }
+    
+    async def _execute_paper_analysis(self, task: Dict) -> Dict[str, Any]:
+        """执行论文分析任务"""
+        input_data = task["input_data"]
+        
+        # 调用Miner Agent
+        miner_result = await self.agents["miner"].run(input_data)
+        task["agent_results"]["miner"] = miner_result
+        
+        return {
+            "task_type": "paper_analysis",
+            "analysis_report": miner_result,
+            "paper_id": input_data.get("paper_id")
+        }
+    
+    async def _execute_writing_assistance(self, task: Dict) -> Dict[str, Any]:
+        """执行写作辅助任务"""
+        input_data = task["input_data"]
+        
+        # 调用Coach Agent
+        coach_result = await self.agents["coach"].run(input_data)
+        task["agent_results"]["coach"] = coach_result
+        
+        return {
+            "task_type": "writing_assistance",
+            "assistance_result": coach_result,
+            "user_id": input_data.get("user_id")
+        }
+    
+    async def _execute_citation_validation(self, task: Dict) -> Dict[str, Any]:
+        """执行引用校验任务"""
+        input_data = task["input_data"]
+        
+        # 调用Validator Agent
+        validator_result = await self.agents["validator"].run(input_data)
+        task["agent_results"]["validator"] = validator_result
+        
+        return {
+            "task_type": "citation_validation",
+            "validation_result": validator_result,
+            "paper_info": input_data.get("paper_info")
+        }
+    
+    async def _execute_full_workflow(self, task: Dict) -> Dict[str, Any]:
+        """执行完整工作流"""
+        input_data = task["input_data"]
+        user_id = input_data.get("user_id")
+        keywords = input_data.get("keywords", [])
+        
+        workflow_result = {
+            "task_type": "full_workflow",
+            "stages": {},
+            "final_papers": [],
+            "analysis_reports": []
+        }
+        
+        try:
+            # Stage 1: 论文抓取
+            self._add_to_history("开始论文抓取阶段")
+            hunting_input = {
+                "keywords": keywords,
+                "max_papers": input_data.get("max_papers", 10),
+                "sources": input_data.get("sources", ["arxiv"])
+            }
+            
+            hunting_result = await self.agents["hunter"].run(hunting_input)
+            workflow_result["stages"]["hunting"] = hunting_result
+            task["agent_results"]["hunter"] = hunting_result
+            
+            downloaded_papers = hunting_result.get("papers", [])
+            workflow_result["final_papers"] = downloaded_papers
+            
+            # Stage 2: 论文分析
+            self._add_to_history("开始论文分析阶段")
+            for paper in downloaded_papers:
+                if paper.get("db_id"):
+                    analysis_input = {
+                        "paper_id": paper["db_id"],
+                        "user_id": user_id,
+                        "analysis_type": "full"
+                    }
+                    
+                    try:
+                        analysis_result = await self.agents["miner"].run(analysis_input)
+                        workflow_result["analysis_reports"].append(analysis_result)
+                    except Exception as e:
+                        self._add_to_history(f"论文分析失败 {paper.get('title', 'Unknown')}: {str(e)}")
+            
+            # Stage 3: 引用校验(可选)
+            if input_data.get("validate_citations", False):
+                self._add_to_history("开始引用校验阶段")
+                for paper in downloaded_papers:
+                    paper_info = {
+                        "title": paper.get("title", ""),
+                        "authors": paper.get("authors", []),
+                        "doi": paper.get("doi", ""),
+                        "year": datetime.now().year
+                    }
+                    
+                    validation_input = {
+                        "paper_info": paper_info,
+                        "formats": ["bibtex", "apa"],
+                        "verify_external": True
+                    }
+                    
+                    try:
+                        validation_result = await self.agents["validator"].run(validation_input)
+                        paper["citations"] = validation_result.get("citations", {})
+                    except Exception as e:
+                        self._add_to_history(f"引用校验失败 {paper.get('title', 'Unknown')}: {str(e)}")
+            
+            self._add_to_history("完整工作流执行完成")
+            
+        except Exception as e:
+            self._add_to_history(f"工作流执行失败: {str(e)}")
+            raise
+        
+        return workflow_result
+    
+    async def start_task_processor(self):
+        """启动任务处理器"""
+        logger.info("启动任务处理器...")
+        
+        while True:
+            try:
+                # 获取任务(按优先级排序)
+                priority, task = await self.task_queue.get()
+                
+                # 异步执行任务
+                asyncio.create_task(self.execute_task(task["id"]))
+                
+            except Exception as e:
+                logger.error(f"任务处理器异常: {str(e)}")
+                await asyncio.sleep(1)
+    
+    async def get_task_status(self, task_id: str) -> Optional[Dict]:
+        """获取任务状态"""
+        if task_id in self.active_tasks:
+            task = self.active_tasks[task_id]
+            return {
+                "id": task["id"],
+                "type": task["type"].value,
+                "status": task["status"].value,
+                "created_at": task["created_at"].isoformat(),
+                "started_at": task["started_at"].isoformat() if task["started_at"] else None,
+                "completed_at": task["completed_at"].isoformat() if task["completed_at"] else None,
+                "priority": task["priority"]
+            }
+        else:
+            # 在历史记录中查找
+            for task in self.task_history:
+                if task["id"] == task_id:
+                    return {
+                        "id": task["id"],
+                        "type": task["type"].value,
+                        "status": task["status"].value,
+                        "created_at": task["created_at"].isoformat(),
+                        "started_at": task["started_at"].isoformat() if task["started_at"] else None,
+                        "completed_at": task["completed_at"].isoformat() if task["completed_at"] else None,
+                        "priority": task["priority"]
+                    }
+        return None
+    
+    async def cancel_task(self, task_id: str) -> bool:
+        """取消任务"""
+        if task_id in self.active_tasks:
+            task = self.active_tasks[task_id]
+            if task["status"] == TaskStatus.PENDING:
+                task["status"] = TaskStatus.CANCELLED
+                task["completed_at"] = datetime.now()
+                
+                # 移动到历史记录
+                self.task_history.append(task.copy())
+                del self.active_tasks[task_id]
+                
+                logger.info(f"任务已取消: {task_id}")
+                return True
+        
+        return False
+    
+    async def get_agent_status(self) -> Dict[str, Any]:
+        """获取所有智能体状态"""
+        agent_status = {}
+        for name, agent in self.agents.items():
+            agent_status[name] = agent.get_status()
+        
+        return {
+            "agents": agent_status,
+            "active_tasks": len(self.active_tasks),
+            "queued_tasks": self.task_queue.qsize(),
+            "completed_tasks": len(self.task_history),
+            "max_concurrent": self.config.concurrent_agents
+        }
+    
+    def add_event_callback(self, event_type: str, callback: Callable):
+        """添加事件回调"""
+        if event_type in self.event_callbacks:
+            self.event_callbacks[event_type].append(callback)
+    
+    async def _trigger_event(self, event_type: str, data: Any):
+        """触发事件"""
+        if event_type in self.event_callbacks:
+            for callback in self.event_callbacks[event_type]:
+                try:
+                    if asyncio.iscoroutinefunction(callback):
+                        await callback(data)
+                    else:
+                        callback(data)
+                except Exception as e:
+                    logger.error(f"事件回调执行失败 {event_type}: {str(e)}")
+    
+    def _add_to_history(self, message: str):
+        """添加到控制器历史记录"""
+        timestamp = datetime.now().isoformat()
+        logger.info(f"[{timestamp}] Controller: {message}")
+    
+    async def shutdown(self):
+        """关闭控制器"""
+        logger.info("关闭Agent Controller...")
+        
+        # 取消所有待处理任务
+        for task_id in list(self.active_tasks.keys()):
+            await self.cancel_task(task_id)
+        
+        # 清理智能体资源
+        for agent in self.agents.values():
+            if hasattr(agent, 'close'):
+                await agent.close()
+        
+        logger.info("Agent Controller已关闭")
+
+# 全局控制器实例
+agent_controller = AgentController()

+ 353 - 0
Co-creation-projects/Apricity-InnocoreAI/agents/hunter.py

@@ -0,0 +1,353 @@
+"""
+InnoCore AI 前哨探员 (Hunter Agent)
+负责每日根据关键词监控ArXiv/IEEE,初筛并下载PDF
+"""
+
+import asyncio
+import aiohttp
+import feedparser
+import re
+from typing import Dict, List, Optional, Any
+from datetime import datetime, timedelta
+import hashlib
+import os
+from urllib.parse import urljoin, quote
+
+from agents.base import BaseAgent
+from core.database import db_manager
+from core.exceptions import AgentException, ExternalAPIException
+
+class HunterAgent(BaseAgent):
+    """前哨探员智能体"""
+    
+    def __init__(self, llm=None):
+        super().__init__("Hunter", llm)
+        self.arxiv_base_url = "http://export.arxiv.org/api/query"
+        self.ieee_base_url = "https://ieeexploreapi.ieee.org/api/v1"
+        self.download_dir = "downloads/papers"
+        
+        # 确保下载目录存在
+        os.makedirs(self.download_dir, exist_ok=True)
+        
+        # 添加工具
+        self.add_tool("search_arxiv", self._search_arxiv, "搜索ArXiv论文")
+        self.add_tool("search_ieee", self._search_ieee, "搜索IEEE论文")
+        self.add_tool("download_pdf", self._download_pdf, "下载PDF文件")
+        self.add_tool("extract_metadata", self._extract_metadata, "提取论文元数据")
+    
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        """执行论文抓取任务"""
+        await self.validate_input(input_data)
+        
+        self.set_state("running")
+        
+        try:
+            keywords = input_data["keywords"]
+            max_papers = input_data.get("max_papers", 20)
+            sources = input_data.get("sources", ["arxiv", "ieee"])
+            days_back = input_data.get("days_back", 1)
+            
+            all_papers = []
+            
+            # 搜索不同来源
+            if "arxiv" in sources:
+                arxiv_papers = await self._search_papers_from_arxiv(keywords, max_papers, days_back)
+                all_papers.extend(arxiv_papers)
+            
+            if "ieee" in sources:
+                ieee_papers = await self._search_papers_from_ieee(keywords, max_papers, days_back)
+                all_papers.extend(ieee_papers)
+            
+            # 去重和筛选
+            unique_papers = self._deduplicate_papers(all_papers)
+            filtered_papers = await self._filter_papers(unique_papers, keywords)
+            
+            # 下载PDF
+            downloaded_papers = []
+            for paper in filtered_papers[:max_papers]:
+                try:
+                    downloaded_paper = await self._download_and_save_paper(paper)
+                    if downloaded_paper:
+                        downloaded_papers.append(downloaded_paper)
+                except Exception as e:
+                    self._add_to_history(f"下载论文失败 {paper.get('title', 'Unknown')}: {str(e)}")
+            
+            self.set_state("completed")
+            
+            return {
+                "status": "success",
+                "total_found": len(all_papers),
+                "unique_papers": len(unique_papers),
+                "filtered_papers": len(filtered_papers),
+                "downloaded_papers": len(downloaded_papers),
+                "papers": downloaded_papers
+            }
+            
+        except Exception as e:
+            self.set_state("error")
+            raise AgentException(f"Hunter Agent执行失败: {str(e)}")
+    
+    def get_required_fields(self) -> List[str]:
+        """获取必需的输入字段"""
+        return ["keywords"]
+    
+    async def _search_papers_from_arxiv(self, keywords: List[str], max_papers: int, days_back: int) -> List[Dict]:
+        """从ArXiv搜索论文"""
+        papers = []
+        
+        # 构建查询字符串
+        query_parts = []
+        for keyword in keywords:
+            query_parts.append(f'all:"{keyword}"')
+        query = " OR ".join(query_parts)
+        
+        # 添加时间过滤
+        date_filter = ""
+        if days_back > 0:
+            start_date = (datetime.now() - timedelta(days=days_back)).strftime("%Y%m%d")
+            date_filter = f"submittedDate:[{start_filter}0000 TO {datetime.now().strftime('%Y%m%d')}2359]"
+        
+        params = {
+            "search_query": query,
+            "start": 0,
+            "max_results": max_papers * 2,  # 获取更多结果以便筛选
+            "sortBy": "submittedDate",
+            "sortOrder": "descending"
+        }
+        
+        try:
+            async with aiohttp.ClientSession() as session:
+                async with session.get(self.arxiv_base_url, params=params) as response:
+                    if response.status != 200:
+                        raise ExternalAPIException(f"ArXiv API请求失败: {response.status}")
+                    
+                    xml_content = await response.text()
+                    feed = feedparser.parse(xml_content)
+                    
+                    for entry in feed.entries:
+                        paper = {
+                            "id": entry.id.split("/")[-1],
+                            "title": entry.title,
+                            "authors": [author.name for author in entry.authors],
+                            "abstract": entry.summary,
+                            "published": entry.published,
+                            "pdf_url": entry.link.replace('/abs/', '/pdf/') + '.pdf',
+                            "source": "arxiv",
+                            "doi": entry.get('arxiv_doi', ''),
+                            "categories": [tag.term for tag in entry.tags]
+                        }
+                        
+                        papers.append(paper)
+                        
+        except Exception as e:
+            self._add_to_history(f"ArXiv搜索失败: {str(e)}")
+        
+        return papers
+    
+    async def _search_papers_from_ieee(self, keywords: List[str], max_papers: int, days_back: int) -> List[Dict]:
+        """从IEEE搜索论文"""
+        papers = []
+        
+        # IEEE API需要API key,这里提供基础实现框架
+        config = self.config.external_apis
+        
+        if not config.ieee_base_url:
+            self._add_to_history("IEEE API配置缺失,跳过IEEE搜索")
+            return papers
+        
+        # 构建查询参数
+        query = " OR ".join([f'"All Meta Data:{keyword}"' for keyword in keywords])
+        
+        params = {
+            "apikey": config.ieee_api_key or "",
+            "querytext": query,
+            "max_records": max_papers * 2,
+            "start_record": 1,
+            "sort_order": "desc",
+            "sort_field": "publication_date"
+        }
+        
+        try:
+            async with aiohttp.ClientSession() as session:
+                async with session.get(self.ieee_base_url, params=params) as response:
+                    if response.status != 200:
+                        raise ExternalAPIException(f"IEEE API请求失败: {response.status}")
+                    
+                    data = await response.json()
+                    
+                    for article in data.get("articles", []):
+                        paper = {
+                            "id": article.get("article_number", ""),
+                            "title": article.get("title", ""),
+                            "authors": [author.get("full_name", "") for author in article.get("authors", {}).get("authors", [])],
+                            "abstract": article.get("abstract", ""),
+                            "published": article.get("publication_date", ""),
+                            "pdf_url": article.get("pdf_url", ""),
+                            "source": "ieee",
+                            "doi": article.get("doi", ""),
+                            "categories": article.get("index_terms", {}).get("ieee_terms", {}).get("terms", [])
+                        }
+                        
+                        papers.append(paper)
+                        
+        except Exception as e:
+            self._add_to_history(f"IEEE搜索失败: {str(e)}")
+        
+        return papers
+    
+    def _deduplicate_papers(self, papers: List[Dict]) -> List[Dict]:
+        """去重论文"""
+        seen_titles = set()
+        unique_papers = []
+        
+        for paper in papers:
+            title = paper.get("title", "").lower().strip()
+            title_hash = hashlib.md5(title.encode()).hexdigest()
+            
+            if title_hash not in seen_titles:
+                seen_titles.add(title_hash)
+                unique_papers.append(paper)
+        
+        return unique_papers
+    
+    async def _filter_papers(self, papers: List[Dict], keywords: List[str]) -> List[Dict]:
+        """根据关键词筛选论文"""
+        filtered_papers = []
+        
+        for paper in papers:
+            title = paper.get("title", "").lower()
+            abstract = paper.get("abstract", "").lower()
+            combined_text = f"{title} {abstract}"
+            
+            # 计算关键词匹配分数
+            score = 0
+            for keyword in keywords:
+                keyword_lower = keyword.lower()
+                if keyword_lower in title:
+                    score += 2  # 标题匹配权重更高
+                if keyword_lower in abstract:
+                    score += 1
+            
+            # 设定阈值
+            if score >= 1:
+                paper["relevance_score"] = score
+                filtered_papers.append(paper)
+        
+        # 按相关性分数排序
+        filtered_papers.sort(key=lambda x: x.get("relevance_score", 0), reverse=True)
+        
+        return filtered_papers
+    
+    async def _download_and_save_paper(self, paper: Dict) -> Optional[Dict]:
+        """下载并保存论文"""
+        pdf_url = paper.get("pdf_url")
+        if not pdf_url:
+            return None
+        
+        try:
+            # 生成文件名
+            safe_title = re.sub(r'[^\w\s-]', '', paper.get("title", "unknown"))[:50]
+            filename = f"{paper['id']}_{safe_title}.pdf"
+            file_path = os.path.join(self.download_dir, filename)
+            
+            # 检查文件是否已存在
+            if os.path.exists(file_path):
+                self._add_to_history(f"论文已存在: {filename}")
+                paper["file_path"] = file_path
+                return paper
+            
+            # 下载PDF
+            async with aiohttp.ClientSession() as session:
+                async with session.get(pdf_url) as response:
+                    if response.status == 200:
+                        content = await response.read()
+                        
+                        with open(file_path, 'wb') as f:
+                            f.write(content)
+                        
+                        # 计算文件哈希
+                        content_hash = hashlib.sha256(content).hexdigest()
+                        
+                        # 更新论文信息
+                        paper["file_path"] = file_path
+                        paper["content_hash"] = content_hash
+                        paper["file_size"] = len(content)
+                        
+                        # 保存到数据库
+                        await self._save_paper_to_db(paper)
+                        
+                        self._add_to_history(f"成功下载论文: {filename}")
+                        return paper
+                    else:
+                        self._add_to_history(f"下载失败,HTTP状态码: {response.status}")
+                        return None
+                        
+        except Exception as e:
+            self._add_to_history(f"下载论文异常: {str(e)}")
+            return None
+    
+    async def _save_paper_to_db(self, paper: Dict):
+        """保存论文到数据库"""
+        try:
+            # 检查是否已存在
+            existing_paper = await db_manager.get_paper_by_hash(paper.get("content_hash"))
+            if existing_paper:
+                self._add_to_history(f"论文已存在于数据库: {paper.get('title')}")
+                return
+            
+            # 创建论文记录
+            paper_id = await db_manager.create_paper(
+                title=paper.get("title", ""),
+                authors=paper.get("authors", []),
+                abstract=paper.get("abstract", ""),
+                doi=paper.get("doi", ""),
+                file_path=paper.get("file_path", ""),
+                content_hash=paper.get("content_hash", ""),
+                is_preset=False
+            )
+            
+            paper["db_id"] = paper_id
+            self._add_to_history(f"论文已保存到数据库: {paper_id}")
+            
+        except Exception as e:
+            self._add_to_history(f"保存论文到数据库失败: {str(e)}")
+    
+    # 工具方法
+    async def _search_arxiv(self, query: str) -> List[Dict]:
+        """搜索ArXiv工具"""
+        keywords = [kw.strip() for kw in query.split(",")]
+        return await self._search_papers_from_arxiv(keywords, 10, 7)
+    
+    async def _search_ieee(self, query: str) -> List[Dict]:
+        """搜索IEEE工具"""
+        keywords = [kw.strip() for kw in query.split(",")]
+        return await self._search_papers_from_ieee(keywords, 10, 7)
+    
+    async def _download_pdf(self, pdf_url: str) -> str:
+        """下载PDF工具"""
+        try:
+            async with aiohttp.ClientSession() as session:
+                async with session.get(pdf_url) as response:
+                    if response.status == 200:
+                        content = await response.read()
+                        filename = f"download_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
+                        file_path = os.path.join(self.download_dir, filename)
+                        
+                        with open(file_path, 'wb') as f:
+                            f.write(content)
+                        
+                        return file_path
+                    else:
+                        return f"下载失败,状态码: {response.status}"
+        except Exception as e:
+            return f"下载异常: {str(e)}"
+    
+    async def _extract_metadata(self, file_path: str) -> Dict:
+        """提取论文元数据工具"""
+        # 这里应该使用PDF解析库提取元数据
+        # 暂时返回基础信息
+        return {
+            "file_path": file_path,
+            "file_size": os.path.getsize(file_path) if os.path.exists(file_path) else 0,
+            "extracted_at": datetime.now().isoformat()
+        }

+ 416 - 0
Co-creation-projects/Apricity-InnocoreAI/agents/miner.py

@@ -0,0 +1,416 @@
+"""
+InnoCore AI 洞察专家 (Miner Agent)
+核心大脑。负责阅读、理解、检索历史库、对比分析并生成报告
+"""
+
+import asyncio
+from typing import Dict, List, Optional, Any
+import json
+import re
+from datetime import datetime
+
+from agents.base import BaseAgent
+from core.database import db_manager
+from core.vector_store import vector_store_manager
+from core.exceptions import AgentException
+
+class MinerAgent(BaseAgent):
+    """洞察专家智能体"""
+    
+    def __init__(self, llm=None):
+        super().__init__("Miner", llm)
+        
+        # 添加工具
+        self.add_tool("parse_pdf", self._parse_pdf, "解析PDF文件")
+        self.add_tool("search_memory", self._search_memory, "搜索记忆库")
+        self.add_tool("compare_papers", self._compare_papers, "对比论文")
+        self.add_tool("generate_report", self._generate_report, "生成分析报告")
+    
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        """执行论文分析和创新点挖掘任务"""
+        await self.validate_input(input_data)
+        
+        self.set_state("running")
+        
+        try:
+            paper_id = input_data["paper_id"]
+            user_id = input_data.get("user_id")
+            analysis_type = input_data.get("analysis_type", "full")  # full, quick, innovation_only
+            
+            # 获取论文信息
+            paper = await db_manager.get_paper(paper_id)
+            if not paper:
+                raise AgentException(f"论文不存在: {paper_id}")
+            
+            self._add_to_history(f"开始分析论文: {paper['title']}")
+            
+            # 1. 解析PDF内容
+            parsed_content = await self._parse_paper_content(paper)
+            
+            # 2. 检索相关历史论文
+            related_papers = await self._find_related_papers(
+                paper["title"], 
+                paper["abstract"], 
+                user_id
+            )
+            
+            # 3. 进行对比分析
+            comparison_result = await self._perform_comparison_analysis(
+                parsed_content, 
+                related_papers
+            )
+            
+            # 4. 生成分析报告
+            report = await self._create_analysis_report(
+                paper, 
+                parsed_content, 
+                related_papers, 
+                comparison_result,
+                user_id
+            )
+            
+            # 5. 保存报告到数据库
+            report_id = await self._save_analysis_report(paper_id, report, user_id)
+            
+            # 6. 更新向量库
+            await self._update_vector_store(paper_id, paper, parsed_content, user_id)
+            
+            self.set_state("completed")
+            
+            return {
+                "status": "success",
+                "paper_id": paper_id,
+                "report_id": report_id,
+                "analysis_type": analysis_type,
+                "parsed_content": {
+                    "sections": list(parsed_content.get("sections", {}).keys()),
+                    "word_count": parsed_content.get("word_count", 0)
+                },
+                "related_papers_count": len(related_papers),
+                "report_summary": {
+                    "summary": report.get("summary", "")[:200] + "...",
+                    "innovation_points": len(report.get("innovation_points", [])),
+                    "limitations": len(report.get("limitations", [])),
+                    "future_ideas": len(report.get("future_ideas", []))
+                }
+            }
+            
+        except Exception as e:
+            self.set_state("error")
+            raise AgentException(f"Miner Agent执行失败: {str(e)}")
+    
+    def get_required_fields(self) -> List[str]:
+        """获取必需的输入字段"""
+        return ["paper_id"]
+    
+    async def _parse_paper_content(self, paper: Dict) -> Dict[str, Any]:
+        """解析论文内容"""
+        file_path = paper.get("file_path")
+        if not file_path:
+            # 如果没有PDF文件,使用标题和摘要
+            return {
+                "title": paper.get("title", ""),
+                "abstract": paper.get("abstract", ""),
+                "sections": {
+                    "abstract": paper.get("abstract", ""),
+                    "introduction": "",
+                    "method": "",
+                    "experiment": "",
+                    "conclusion": ""
+                },
+                "word_count": len(paper.get("abstract", "").split()),
+                "parsing_method": "metadata_only"
+            }
+        
+        # 这里应该使用专门的PDF解析库
+        # 暂时返回模拟的结构化内容
+        return await self._extract_structured_content(file_path)
+    
+    async def _extract_structured_content(self, file_path: str) -> Dict[str, Any]:
+        """提取结构化内容"""
+        try:
+            # 这里应该集成Nougat或PyMuPDF进行深度解析
+            # 暂时返回模拟数据
+            mock_content = {
+                "title": "Sample Paper Title",
+                "abstract": "This is a sample abstract...",
+                "sections": {
+                    "introduction": "In this paper, we propose...",
+                    "method": "Our method consists of...",
+                    "experiment": "We conducted experiments...",
+                    "conclusion": "The results show that..."
+                },
+                "word_count": 1500,
+                "parsing_method": "mock_parser"
+            }
+            
+            self._add_to_history(f"PDF解析完成: {file_path}")
+            return mock_content
+            
+        except Exception as e:
+            self._add_to_history(f"PDF解析失败: {str(e)}")
+            return {
+                "title": "",
+                "abstract": "",
+                "sections": {},
+                "word_count": 0,
+                "parsing_method": "failed"
+            }
+    
+    async def _find_related_papers(self, title: str, abstract: str, user_id: str = None) -> List[Dict]:
+        """查找相关论文"""
+        try:
+            # 构建查询
+            query = f"{title} {abstract}"
+            
+            # 执行混合搜索
+            search_results = await vector_store_manager.hybrid_search(
+                query=query,
+                user_id=user_id,
+                top_k=10,
+                include_l1=True,
+                include_l2=bool(user_id)
+            )
+            
+            # 获取详细论文信息
+            related_papers = []
+            for result in search_results:
+                payload = result["payload"]
+                paper_id = payload.get("paper_id")
+                
+                if paper_id:
+                    paper_info = await db_manager.get_paper(paper_id)
+                    if paper_info:
+                        paper_info["similarity_score"] = result["score"]
+                        paper_info["collection_type"] = result["collection_type"]
+                        related_papers.append(paper_info)
+            
+            self._add_to_history(f"找到 {len(related_papers)} 篇相关论文")
+            return related_papers
+            
+        except Exception as e:
+            self._add_to_history(f"搜索相关论文失败: {str(e)}")
+            return []
+    
+    async def _perform_comparison_analysis(self, current_paper: Dict, related_papers: List[Dict]) -> Dict[str, Any]:
+        """执行对比分析"""
+        if not related_papers:
+            return {
+                "comparison_summary": "未找到相关论文进行对比",
+                "unique_contributions": [],
+                "similar_works": [],
+                "gaps_identified": []
+            }
+        
+        # 构建对比分析的prompt
+        comparison_prompt = f"""
+        请分析当前论文与历史相关论文的对比情况:
+        
+        当前论文:
+        标题:{current_paper.get('title', '')}
+        摘要:{current_paper.get('abstract', '')}
+        主要内容:{str(current_paper.get('sections', {}))[:1000]}...
+        
+        相关论文:
+        {self._format_related_papers_for_comparison(related_papers[:5])}
+        
+        请从以下角度进行对比分析:
+        1. 方法的创新性和改进点
+        2. 实验设计的优势
+        3. 与现有工作的区别
+        4. 可能的研究空白
+        
+        请以JSON格式返回分析结果。
+        """
+        
+        try:
+            response = await self.think(comparison_prompt)
+            
+            # 尝试解析JSON响应
+            try:
+                comparison_result = json.loads(response)
+            except json.JSONDecodeError:
+                # 如果JSON解析失败,使用文本解析
+                comparison_result = self._parse_text_comparison(response)
+            
+            self._add_to_history("对比分析完成")
+            return comparison_result
+            
+        except Exception as e:
+            self._add_to_history(f"对比分析失败: {str(e)}")
+            return {
+                "comparison_summary": "对比分析过程中出现错误",
+                "unique_contributions": [],
+                "similar_works": [],
+                "gaps_identified": []
+            }
+    
+    def _format_related_papers_for_comparison(self, papers: List[Dict]) -> str:
+        """格式化相关论文用于对比"""
+        formatted = []
+        for i, paper in enumerate(papers, 1):
+            formatted.append(f"""
+            论文{i}:
+            标题:{paper.get('title', '')}
+            摘要:{paper.get('abstract', '')[:300]}...
+            相似度:{paper.get('similarity_score', 0):.3f}
+            """)
+        return "\n".join(formatted)
+    
+    def _parse_text_comparison(self, text: str) -> Dict[str, Any]:
+        """解析文本格式的对比结果"""
+        # 简单的文本解析逻辑
+        return {
+            "comparison_summary": text[:500],
+            "unique_contributions": ["基于文本分析的创新点"],
+            "similar_works": ["相关研究工作"],
+            "gaps_identified": ["研究空白识别"]
+        }
+    
+    async def _create_analysis_report(self, paper: Dict, parsed_content: Dict, 
+                                    related_papers: List[Dict], comparison_result: Dict,
+                                    user_id: str = None) -> Dict[str, Any]:
+        """创建分析报告"""
+        
+        report_prompt = f"""
+        基于以下信息,生成一份详细的论文分析报告:
+        
+        论文信息:
+        标题:{paper.get('title', '')}
+        作者:{', '.join(paper.get('authors', []))}
+        摘要:{paper.get('abstract', '')}
+        
+        解析内容:
+        {str(parsed_content.get('sections', {}))[:1500]}...
+        
+        对比分析结果:
+        {str(comparison_result)[:1000]}...
+        
+        请生成包含以下部分的报告:
+        1. Summary - 论文主要贡献和方法概述
+        2. Innovation - 相比相关论文的创新点
+        3. Limitation - 当前研究的局限性
+        4. Future Ideas - 基于分析的未来研究方向建议
+        
+        请以JSON格式返回报告。
+        """
+        
+        try:
+            response = await self.think(report_prompt)
+            
+            # 尝试解析JSON响应
+            try:
+                report = json.loads(response)
+            except json.JSONDecodeError:
+                # 如果JSON解析失败,生成默认报告
+                report = self._generate_default_report(paper, parsed_content, comparison_result)
+            
+            # 添加元数据
+            report.update({
+                "paper_id": paper.get("id"),
+                "generated_for_user_id": user_id,
+                "generated_at": datetime.now().isoformat(),
+                "related_papers_count": len(related_papers),
+                "analysis_method": "miner_agent"
+            })
+            
+            self._add_to_history("分析报告生成完成")
+            return report
+            
+        except Exception as e:
+            self._add_to_history(f"生成分析报告失败: {str(e)}")
+            return self._generate_default_report(paper, parsed_content, comparison_result)
+    
+    def _generate_default_report(self, paper: Dict, parsed_content: Dict, comparison_result: Dict) -> Dict[str, Any]:
+        """生成默认报告"""
+        return {
+            "summary": f"本文提出了{paper.get('title', '')}相关的研究工作。",
+            "innovation_points": ["需要进一步分析的创新点"],
+            "limitations": ["识别出的研究局限性"],
+            "future_ideas": ["建议的未来研究方向"],
+            "paper_id": paper.get("id"),
+            "generated_at": datetime.now().isoformat(),
+            "analysis_method": "default"
+        }
+    
+    async def _save_analysis_report(self, paper_id: str, report: Dict, user_id: str = None) -> str:
+        """保存分析报告到数据库"""
+        try:
+            report_id = await db_manager.create_analysis_report(
+                paper_id=paper_id,
+                summary=report.get("summary", ""),
+                innovation_point=json.dumps(report.get("innovation_points", []), ensure_ascii=False),
+                limitation=json.dumps(report.get("limitations", []), ensure_ascii=False),
+                future_idea=json.dumps(report.get("future_ideas", []), ensure_ascii=False),
+                vector_ids=report.get("vector_ids", {}),
+                user_id=user_id
+            )
+            
+            self._add_to_history(f"分析报告已保存: {report_id}")
+            return report_id
+            
+        except Exception as e:
+            self._add_to_history(f"保存分析报告失败: {str(e)}")
+            return ""
+    
+    async def _update_vector_store(self, paper_id: str, paper: Dict, parsed_content: Dict, user_id: str = None):
+        """更新向量库"""
+        try:
+            title = paper.get("title", "")
+            abstract = paper.get("abstract", "")
+            
+            # 组合内容
+            content = f"{title} {abstract}"
+            sections = parsed_content.get("sections", {})
+            if sections:
+                content += " " + " ".join(sections.values())
+            
+            # 添加到L2用户库
+            if user_id:
+                await vector_store_manager.add_to_l2(
+                    user_id=user_id,
+                    paper_id=paper_id,
+                    title=title,
+                    abstract=abstract,
+                    content=content,
+                    metadata={
+                        "authors": paper.get("authors", []),
+                        "sections": list(sections.keys()),
+                        "word_count": parsed_content.get("word_count", 0),
+                        "analysis_date": datetime.now().isoformat()
+                    }
+                )
+                self._add_to_history(f"论文已添加到用户向量库: {user_id}")
+            
+        except Exception as e:
+            self._add_to_history(f"更新向量库失败: {str(e)}")
+    
+    # 工具方法
+    async def _parse_pdf(self, file_path: str) -> Dict:
+        """解析PDF工具"""
+        return await self._extract_structured_content(file_path)
+    
+    async def _search_memory(self, query: str, user_id: str = None) -> List[Dict]:
+        """搜索记忆库工具"""
+        try:
+            results = await vector_store_manager.hybrid_search(
+                query=query,
+                user_id=user_id,
+                top_k=5
+            )
+            return [{"id": r["id"], "score": r["score"], "payload": r["payload"]} for r in results]
+        except Exception as e:
+            return [{"error": str(e)}]
+    
+    async def _compare_papers(self, current_paper: Dict, related_papers: List[Dict]) -> Dict:
+        """对比论文工具"""
+        return await self._perform_comparison_analysis(current_paper, related_papers)
+    
+    async def _generate_report(self, paper_info: Dict, analysis_result: Dict) -> Dict:
+        """生成报告工具"""
+        return await self._create_analysis_report(
+            paper_info, 
+            analysis_result.get("parsed_content", {}),
+            analysis_result.get("related_papers", []),
+            analysis_result.get("comparison_result", {})
+        )

+ 610 - 0
Co-creation-projects/Apricity-InnocoreAI/agents/validator.py

@@ -0,0 +1,610 @@
+"""
+InnoCore AI 校验官 (Validator Agent)
+负责生成引用格式并联网校验元数据
+"""
+
+import asyncio
+import aiohttp
+import re
+import json
+from typing import Dict, List, Optional, Any
+from datetime import datetime
+import hashlib
+
+from agents.base import BaseAgent
+from core.database import db_manager
+from core.exceptions import AgentException, ExternalAPIException
+
+class ValidatorAgent(BaseAgent):
+    """校验官智能体"""
+    
+    def __init__(self, llm=None):
+        super().__init__("Validator", llm)
+        
+        # API配置
+        self.crossref_base_url = "https://api.crossref.org/works"
+        self.google_scholar_url = "https://serpapi.com/search"
+        
+        # 添加工具
+        self.add_tool("generate_bibtex", self._generate_bibtex, "生成BibTeX引用")
+        self.add_tool("generate_apa", self._generate_apa, "生成APA格式引用")
+        self.add_tool("generate_ieee", self._generate_ieee, "生成IEEE格式引用")
+        self.add_tool("verify_metadata", self._verify_metadata, "校验元数据")
+        self.add_tool("crossref_lookup", self._crossref_lookup, "CrossRef查询")
+        self.add_tool("scholar_lookup", self._scholar_lookup, "Google Scholar查询")
+    
+    async def run(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
+        """执行引用校验任务"""
+        await self.validate_input(input_data)
+        
+        self.set_state("running")
+        
+        try:
+            paper_info = input_data["paper_info"]
+            formats = input_data.get("formats", ["bibtex", "apa", "ieee"])
+            verify_external = input_data.get("verify_external", True)
+            
+            # 1. 生成多种格式的引用
+            citations = await self._generate_citations(paper_info, formats)
+            
+            # 2. 外部校验元数据
+            verification_result = {}
+            if verify_external:
+                verification_result = await self._verify_paper_metadata(paper_info)
+            
+            # 3. 合并和更新引用信息
+            final_citations = await self._merge_citation_data(
+                citations, 
+                verification_result, 
+                paper_info
+            )
+            
+            # 4. 缓存结果
+            await self._cache_citation_results(final_citations)
+            
+            self.set_state("completed")
+            
+            return {
+                "status": "success",
+                "paper_info": paper_info,
+                "citations": final_citations,
+                "verification": verification_result,
+                "formats_generated": list(citations.keys()),
+                "verification_status": verification_result.get("status", "unknown"),
+                "timestamp": datetime.now().isoformat()
+            }
+            
+        except Exception as e:
+            self.set_state("error")
+            raise AgentException(f"Validator Agent执行失败: {str(e)}")
+    
+    def get_required_fields(self) -> List[str]:
+        """获取必需的输入字段"""
+        return ["paper_info"]
+    
+    async def _generate_citations(self, paper_info: Dict, formats: List[str]) -> Dict[str, Any]:
+        """生成多种格式的引用"""
+        citations = {}
+        
+        for format_type in formats:
+            try:
+                if format_type.lower() == "bibtex":
+                    citations["bibtex"] = await self._generate_bibtex_citation(paper_info)
+                elif format_type.lower() == "apa":
+                    citations["apa"] = await self._generate_apa_citation(paper_info)
+                elif format_type.lower() == "ieee":
+                    citations["ieee"] = await self._generate_ieee_citation(paper_info)
+                else:
+                    self._add_to_history(f"不支持的引用格式: {format_type}")
+                    
+            except Exception as e:
+                self._add_to_history(f"生成{format_type}格式失败: {str(e)}")
+                citations[format_type] = f"生成失败: {str(e)}"
+        
+        return citations
+    
+    async def _generate_bibtex_citation(self, paper_info: Dict) -> str:
+        """生成BibTeX格式引用"""
+        # 生成引用键
+        first_author = paper_info.get("authors", [""])[0]
+        if isinstance(first_author, str):
+            last_name = first_author.split()[-1].lower()
+        else:
+            last_name = "unknown"
+        
+        year = paper_info.get("year", datetime.now().year)
+        title_words = paper_info.get("title", "").split()[:3]
+        title_key = "".join([w.lower() for w in title_words if w.isalpha()])
+        
+        citation_key = f"{last_name}{year}{title_key}"
+        
+        # 构建BibTeX条目
+        entry_type = self._determine_entry_type(paper_info)
+        
+        bibtex = f"@{entry_type}{{{citation_key},\n"
+        
+        # 添加作者
+        authors = paper_info.get("authors", [])
+        if authors:
+            bibtex += f"  author = {{{self._format_bibtex_authors(authors)}}},\n"
+        
+        # 添加标题
+        title = paper_info.get("title", "")
+        if title:
+            bibtex += f"  title = {{{title}}},\n"
+        
+        # 添加期刊/会议信息
+        if entry_type == "article":
+            journal = paper_info.get("journal", "")
+            if journal:
+                bibtex += f"  journal = {{{journal}}},\n"
+            
+            volume = paper_info.get("volume", "")
+            if volume:
+                bibtex += f"  volume = {{{volume}}},\n"
+            
+            number = paper_info.get("number", "")
+            if number:
+                bibtex += f"  number = {{{number}}},\n"
+            
+            pages = paper_info.get("pages", "")
+            if pages:
+                bibtex += f"  pages = {{{pages}}},\n"
+        
+        elif entry_type == "inproceedings":
+            booktitle = paper_info.get("booktitle", "")
+            if booktitle:
+                bibtex += f"  booktitle = {{{booktitle}}},\n"
+            
+            pages = paper_info.get("pages", "")
+            if pages:
+                bibtex += f"  pages = {{{pages}}},\n"
+        
+        # 添加年份
+        if year:
+            bibtex += f"  year = {{{year}}},\n"
+        
+        # 添加DOI
+        doi = paper_info.get("doi", "")
+        if doi:
+            bibtex += f"  doi = {{{doi}}},\n"
+        
+        # 添加URL
+        url = paper_info.get("url", "")
+        if url:
+            bibtex += f"  url = {{{url}}},\n"
+        
+        # 移除最后的逗号并关闭
+        bibtex = bibtex.rstrip(",\n") + "\n}"
+        
+        return bibtex
+    
+    async def _generate_apa_citation(self, paper_info: Dict) -> str:
+        """生成APA格式引用"""
+        authors = paper_info.get("authors", [])
+        year = paper_info.get("year", "")
+        title = paper_info.get("title", "")
+        
+        # 格式化作者
+        if len(authors) == 0:
+            author_text = ""
+        elif len(authors) == 1:
+            author_text = authors[0]
+        elif len(authors) == 2:
+            author_text = f"{authors[0]} & {authors[1]}"
+        elif len(authors) <= 7:
+            author_text = ", ".join(authors[:-1]) + f", & {authors[-1]}"
+        else:
+            author_text = ", ".join(authors[:6]) + f", ... {authors[-1]}"
+        
+        # 构建APA引用
+        if year:
+            apa_citation = f"{author_text} ({year}). {title}."
+        else:
+            apa_citation = f"{author_text}. {title}."
+        
+        # 添加期刊信息
+        journal = paper_info.get("journal", "")
+        volume = paper_info.get("volume", "")
+        number = paper_info.get("number", "")
+        pages = paper_info.get("pages", "")
+        
+        if journal:
+            if volume and number:
+                apa_citation += f" *{journal}*, *{volume}({number})*"
+            elif volume:
+                apa_citation += f" *{journal}*, *{volume}*"
+            else:
+                apa_citation += f" *{journal}*"
+            
+            if pages:
+                apa_citation += f", {pages}."
+            else:
+                apa_citation += "."
+        
+        # 添加DOI
+        doi = paper_info.get("doi", "")
+        if doi:
+            apa_citation += f" https://doi.org/{doi}"
+        
+        return apa_citation
+    
+    async def _generate_ieee_citation(self, paper_info: Dict) -> str:
+        """生成IEEE格式引用"""
+        authors = paper_info.get("authors", [])
+        year = paper_info.get("year", "")
+        title = paper_info.get("title", "")
+        
+        # 格式化作者(IEEE使用首字母缩写)
+        ieee_authors = []
+        for author in authors[:3]:  # IEEE通常只列出前3个作者
+            if isinstance(author, str):
+                parts = author.split()
+                if len(parts) >= 2:
+                    last_name = parts[-1]
+                    initials = " ".join([p[0] + "." for p in parts[:-1]])
+                    ieee_authors.append(f"{initials} {last_name}")
+                else:
+                    ieee_authors.append(author)
+        
+        if len(authors) > 3:
+            ieee_authors.append("et al.")
+        
+        author_text = ", ".join(ieee_authors)
+        
+        # 构建IEEE引用
+        if title:
+            ieee_citation = f'"{title},"'
+        else:
+            ieee_citation = ""
+        
+        # 添加期刊信息
+        journal = paper_info.get("journal", "")
+        volume = paper_info.get("volume", "")
+        number = paper_info.get("number", "")
+        pages = paper_info.get("pages", "")
+        
+        if journal:
+            if volume and number:
+                ieee_citation += f" *{journal}*, vol. {volume}, no. {number}"
+            elif volume:
+                ieee_citation += f" *{journal}*, vol. {volume}"
+            else:
+                ieee_citation += f" *{journal}*"
+            
+            if pages:
+                ieee_citation += f", pp. {pages}"
+        
+        # 添加年份和月份
+        if year:
+            month = paper_info.get("month", "")
+            if month:
+                ieee_citation += f", {month}. {year}."
+            else:
+                ieee_citation += f", {year}."
+        
+        # 添加DOI
+        doi = paper_info.get("doi", "")
+        if doi:
+            ieee_citation += f" doi: {doi}"
+        
+        return ieee_citation
+    
+    def _determine_entry_type(self, paper_info: Dict) -> str:
+        """确定BibTeX条目类型"""
+        if paper_info.get("journal"):
+            return "article"
+        elif paper_info.get("booktitle"):
+            return "inproceedings"
+        elif paper_info.get("publisher"):
+            return "book"
+        else:
+            return "misc"
+    
+    def _format_bibtex_authors(self, authors: List[str]) -> str:
+        """格式化BibTeX作者"""
+        formatted_authors = []
+        for author in authors:
+            if isinstance(author, str):
+                # 将 "First Last" 转换为 "Last, First"
+                parts = author.split()
+                if len(parts) >= 2:
+                    formatted_authors.append(f"{parts[-1]}, {' '.join(parts[:-1])}")
+                else:
+                    formatted_authors.append(author)
+            else:
+                formatted_authors.append(str(author))
+        
+        return " and ".join(formatted_authors)
+    
+    async def _verify_paper_metadata(self, paper_info: Dict) -> Dict[str, Any]:
+        """校验论文元数据"""
+        verification_result = {
+            "status": "pending",
+            "crossref_verified": False,
+            "scholar_verified": False,
+            "discrepancies": [],
+            "suggested_corrections": {},
+            "verification_timestamp": datetime.now().isoformat()
+        }
+        
+        doi = paper_info.get("doi", "")
+        title = paper_info.get("title", "")
+        
+        try:
+            # 1. CrossRef校验
+            if doi:
+                crossref_data = await self._crossref_lookup_by_doi(doi)
+                if crossref_data:
+                    verification_result["crossref_verified"] = True
+                    discrepancies = self._compare_metadata(paper_info, crossref_data)
+                    if discrepancies:
+                        verification_result["discrepancies"].extend(discrepancies)
+                        verification_result["suggested_corrections"].update(
+                            self._generate_corrections(discrepancies)
+                        )
+            
+            # 2. Google Scholar校验
+            if title:
+                scholar_data = await self._scholar_lookup_by_title(title)
+                if scholar_data:
+                    verification_result["scholar_verified"] = True
+                    discrepancies = self._compare_metadata(paper_info, scholar_data)
+                    if discrepancies:
+                        verification_result["discrepancies"].extend(discrepancies)
+                        verification_result["suggested_corrections"].update(
+                            self._generate_corrections(discrepancies)
+                        )
+            
+            # 确定最终状态
+            if verification_result["crossref_verified"] or verification_result["scholar_verified"]:
+                if not verification_result["discrepancies"]:
+                    verification_result["status"] = "verified"
+                else:
+                    verification_result["status"] = "discrepancies_found"
+            else:
+                verification_result["status"] = "unverified"
+            
+        except Exception as e:
+            verification_result["status"] = "error"
+            verification_result["error"] = str(e)
+            self._add_to_history(f"元数据校验失败: {str(e)}")
+        
+        return verification_result
+    
+    async def _crossref_lookup_by_doi(self, doi: str) -> Optional[Dict]:
+        """通过DOI查询CrossRef"""
+        try:
+            url = f"{self.crossref_base_url}/{doi}"
+            
+            async with aiohttp.ClientSession() as session:
+                async with session.get(url) as response:
+                    if response.status == 200:
+                        data = await response.json()
+                        return self._parse_crossref_data(data)
+                    else:
+                        self._add_to_history(f"CrossRef查询失败,状态码: {response.status}")
+                        return None
+                        
+        except Exception as e:
+            self._add_to_history(f"CrossRef查询异常: {str(e)}")
+            return None
+    
+    async def _scholar_lookup_by_title(self, title: str) -> Optional[Dict]:
+        """通过标题查询Google Scholar"""
+        try:
+            config = self.config.external_apis
+            if not config.serpapi_key:
+                self._add_to_history("SerpApi key缺失,跳过Google Scholar查询")
+                return None
+            
+            params = {
+                "engine": "google_scholar",
+                "q": title,
+                "api_key": config.serpapi_key
+            }
+            
+            async with aiohttp.ClientSession() as session:
+                async with session.get(self.google_scholar_url, params=params) as response:
+                    if response.status == 200:
+                        data = await response.json()
+                        return self._parse_scholar_data(data)
+                    else:
+                        self._add_to_history(f"Google Scholar查询失败,状态码: {response.status}")
+                        return None
+                        
+        except Exception as e:
+            self._add_to_history(f"Google Scholar查询异常: {str(e)}")
+            return None
+    
+    def _parse_crossref_data(self, data: Dict) -> Dict:
+        """解析CrossRef数据"""
+        message = data.get("message", {})
+        
+        return {
+            "title": " ".join(message.get("title", [])),
+            "authors": [f"{author.get('given', '')} {author.get('family', '')}" 
+                       for author in message.get("author", [])],
+            "year": message.get("published-print", {}).get("date-parts", [[""]])[0][0][:4],
+            "journal": message.get("short-container-title", [""])[0],
+            "volume": message.get("volume", ""),
+            "issue": message.get("issue", ""),
+            "page": message.get("page", ""),
+            "doi": message.get("DOI", ""),
+            "source": "crossref"
+        }
+    
+    def _parse_scholar_data(self, data: Dict) -> Dict:
+        """解析Google Scholar数据"""
+        organic_results = data.get("organic_results", [])
+        if not organic_results:
+            return {}
+        
+        first_result = organic_results[0]
+        
+        # 提取年份
+        publication_info = first_result.get("publication_info", {})
+        year = ""
+        if "summary" in publication_info:
+            year_match = re.search(r'\b(19|20)\d{2}\b', publication_info["summary"])
+            if year_match:
+                year = year_match.group()
+        
+        return {
+            "title": first_result.get("title", ""),
+            "authors": first_result.get("publication_info", {}).get("authors", []),
+            "year": year,
+            "journal": publication_info.get("summary", "").split(",")[0] if publication_info.get("summary") else "",
+            "source": "google_scholar"
+        }
+    
+    def _compare_metadata(self, original: Dict, reference: Dict) -> List[Dict]:
+        """比较元数据差异"""
+        discrepancies = []
+        
+        # 比较标题
+        orig_title = original.get("title", "").lower().strip()
+        ref_title = reference.get("title", "").lower().strip()
+        if orig_title and ref_title and orig_title != ref_title:
+            discrepancies.append({
+                "field": "title",
+                "original": original.get("title", ""),
+                "reference": reference.get("title", ""),
+                "similarity": self._calculate_similarity(orig_title, ref_title)
+            })
+        
+        # 比较作者
+        orig_authors = set([author.lower() for author in original.get("authors", [])])
+        ref_authors = set([author.lower() for author in reference.get("authors", [])])
+        if orig_authors and ref_authors and orig_authors != ref_authors:
+            discrepancies.append({
+                "field": "authors",
+                "original": original.get("authors", []),
+                "reference": reference.get("authors", []),
+                "missing_in_original": list(ref_authors - orig_authors),
+                "extra_in_original": list(orig_authors - ref_authors)
+            })
+        
+        # 比较年份
+        orig_year = str(original.get("year", ""))
+        ref_year = str(reference.get("year", ""))
+        if orig_year and ref_year and orig_year != ref_year:
+            discrepancies.append({
+                "field": "year",
+                "original": orig_year,
+                "reference": ref_year
+            })
+        
+        return discrepancies
+    
+    def _calculate_similarity(self, text1: str, text2: str) -> float:
+        """计算文本相似度"""
+        if not text1 or not text2:
+            return 0.0
+        
+        words1 = set(text1.split())
+        words2 = set(text2.split())
+        
+        intersection = words1.intersection(words2)
+        union = words1.union(words2)
+        
+        return len(intersection) / len(union) if union else 0.0
+    
+    def _generate_corrections(self, discrepancies: List[Dict]) -> Dict:
+        """生成修正建议"""
+        corrections = {}
+        
+        for discrepancy in discrepancies:
+            field = discrepancy["field"]
+            if field == "title" and discrepancy.get("similarity", 0) > 0.8:
+                corrections[field] = discrepancy["reference"]
+            elif field == "year":
+                corrections[field] = discrepancy["reference"]
+            elif field == "authors":
+                # 对于作者,建议使用参考数据的完整列表
+                corrections[field] = discrepancy["reference"]
+        
+        return corrections
+    
+    async def _merge_citation_data(self, citations: Dict, verification: Dict, paper_info: Dict) -> Dict[str, Any]:
+        """合并引用数据"""
+        final_citations = {}
+        
+        for format_type, citation_text in citations.items():
+            if isinstance(citation_text, str) and not citation_text.startswith("生成失败"):
+                # 添加校验状态标记
+                verification_status = verification.get("status", "unknown")
+                
+                if verification_status == "verified":
+                    citation_text += "  % [Verified]"
+                elif verification_status == "discrepancies_found":
+                    citation_text += "  % [Discrepancies Found]"
+                else:
+                    citation_text += "  % [Unverified]"
+                
+                final_citations[format_type] = citation_text
+            else:
+                final_citations[format_type] = citation_text
+        
+        # 添加元数据
+        final_citations["metadata"] = {
+            "original_info": paper_info,
+            "verification": verification,
+            "generated_formats": list(citations.keys()),
+            "generation_timestamp": datetime.now().isoformat()
+        }
+        
+        return final_citations
+    
+    async def _cache_citation_results(self, citations: Dict):
+        """缓存引用结果"""
+        try:
+            metadata = citations.get("metadata", {})
+            original_info = metadata.get("original_info", {})
+            doi = original_info.get("doi", "")
+            
+            if doi:
+                # 缓存BibTeX格式
+                bibtex = citations.get("bibtex", "")
+                if bibtex and not bibtex.startswith("生成失败"):
+                    verification = metadata.get("verification", {})
+                    is_verified = verification.get("status") == "verified"
+                    
+                    await db_manager.cache_reference(
+                        doi=doi,
+                        bibtex=bibtex,
+                        is_verified=is_verified
+                    )
+                    
+                    self._add_to_history(f"引用已缓存: {doi}")
+                    
+        except Exception as e:
+            self._add_to_history(f"缓存引用失败: {str(e)}")
+    
+    # 工具方法
+    async def _generate_bibtex(self, paper_info: Dict) -> str:
+        """生成BibTeX工具"""
+        return await self._generate_bibtex_citation(paper_info)
+    
+    async def _generate_apa(self, paper_info: Dict) -> str:
+        """生成APA工具"""
+        return await self._generate_apa_citation(paper_info)
+    
+    async def _generate_ieee(self, paper_info: Dict) -> str:
+        """生成IEEE工具"""
+        return await self._generate_ieee_citation(paper_info)
+    
+    async def _verify_metadata(self, paper_info: Dict) -> Dict:
+        """校验元数据工具"""
+        return await self._verify_paper_metadata(paper_info)
+    
+    async def _crossref_lookup(self, identifier: str) -> Dict:
+        """CrossRef查询工具"""
+        if identifier.startswith("10."):  # DOI
+            return await self._crossref_lookup_by_doi(identifier)
+        else:
+            return {"error": "请提供有效的DOI"}
+    
+    async def _scholar_lookup(self, title: str) -> Dict:
+        """Google Scholar查询工具"""
+        return await self._scholar_lookup_by_title(title)

+ 11 - 0
Co-creation-projects/Apricity-InnocoreAI/api/__init__.py

@@ -0,0 +1,11 @@
+"""
+InnoCore AI API模块
+"""
+
+try:
+    from .main import app
+    from .routes import *
+    __all__ = ["app"]
+except ImportError:
+    # 当直接导入时,避免相对导入错误
+    pass

+ 164 - 0
Co-creation-projects/Apricity-InnocoreAI/api/main.py

@@ -0,0 +1,164 @@
+"""
+InnoCore API 主应用
+"""
+
+from fastapi import FastAPI, HTTPException, Depends, BackgroundTasks
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import JSONResponse
+from contextlib import asynccontextmanager
+import logging
+import uvicorn
+
+from core.config import get_config
+from core.database import db_manager
+from core.vector_store import vector_store_manager
+from agents.controller import agent_controller
+from .routes import papers, users, tasks, analysis, writing, citations, workflow
+
+# 配置日志
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+@asynccontextmanager
+async def lifespan(app: FastAPI):
+    """应用生命周期管理"""
+    # 启动时初始化
+    logger.info("正在启动InnoCore AI...")
+    
+    # 初始化数据库(可选)
+    try:
+        await db_manager.initialize()
+        logger.info("数据库初始化完成")
+    except Exception as e:
+        logger.warning(f"数据库初始化失败(将以无数据库模式运行): {str(e)}")
+    
+    # 初始化向量存储(可选)
+    try:
+        await vector_store_manager.initialize()
+        logger.info("向量存储初始化完成")
+    except Exception as e:
+        logger.warning(f"向量存储初始化失败(将以无向量存储模式运行): {str(e)}")
+    
+    # 初始化智能体控制器(可选)
+    try:
+        await agent_controller.initialize()
+        logger.info("智能体控制器初始化完成")
+        
+        # 启动任务处理器
+        import asyncio
+        asyncio.create_task(agent_controller.start_task_processor())
+        logger.info("任务处理器已启动")
+    except Exception as e:
+        logger.warning(f"智能体控制器初始化失败: {str(e)}")
+    
+    logger.info("InnoCore AI 启动完成")
+    
+    yield
+    
+    # 关闭时清理
+    logger.info("正在关闭InnoCore AI...")
+    await agent_controller.shutdown()
+    await db_manager.close()
+    await vector_store_manager.close()
+    logger.info("InnoCore AI已关闭")
+
+# 创建FastAPI应用
+app = FastAPI(
+    title="InnoCore AI API",
+    description="智能科研创新助手API",
+    version="0.1.0",
+    lifespan=lifespan
+)
+
+# 配置CORS
+config = get_config()
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=["*"],  # 生产环境应该限制具体域名
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+# 注册路由
+app.include_router(papers.router, prefix="/api/v1/papers", tags=["papers"])
+app.include_router(users.router, prefix="/api/v1/users", tags=["users"])
+app.include_router(tasks.router, prefix="/api/v1/tasks", tags=["tasks"])
+app.include_router(analysis.router, prefix="/api/v1/analysis", tags=["analysis"])
+app.include_router(writing.router, prefix="/api/v1/writing", tags=["writing"])
+app.include_router(citations.router, prefix="/api/v1/citations", tags=["citations"])
+app.include_router(workflow.router, prefix="/api/v1/workflow", tags=["workflow"])
+
+# 挂载静态文件
+from fastapi.staticfiles import StaticFiles
+from fastapi.responses import FileResponse
+import os
+
+# 获取项目根目录
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+FRONTEND_DIR = os.path.join(BASE_DIR, "frontend")
+
+# 挂载静态资源
+if os.path.exists(os.path.join(FRONTEND_DIR, "static")):
+    app.mount("/static", StaticFiles(directory=os.path.join(FRONTEND_DIR, "static")), name="static")
+
+# 根路径 - 返回前端页面
+@app.get("/")
+async def root():
+    """根路径 - 返回前端首页"""
+    index_path = os.path.join(FRONTEND_DIR, "index.html")
+    if os.path.exists(index_path):
+        return FileResponse(index_path)
+    return {
+        "message": "Welcome to InnoCore AI API",
+        "version": "0.1.0",
+        "status": "running"
+    }
+
+# 健康检查
+@app.get("/health")
+async def health_check():
+    """健康检查"""
+    try:
+        # 检查各组件状态
+        agent_status = await agent_controller.get_agent_status()
+        
+        return {
+            "status": "healthy",
+            "timestamp": "2024-01-01T00:00:00Z",
+            "components": {
+                "database": "connected",
+                "vector_store": "connected",
+                "agents": agent_status
+            }
+        }
+    except Exception as e:
+        return JSONResponse(
+            status_code=503,
+            content={
+                "status": "unhealthy",
+                "error": str(e)
+            }
+        )
+
+# 全局异常处理
+@app.exception_handler(Exception)
+async def global_exception_handler(request, exc):
+    """全局异常处理器"""
+    logger.error(f"全局异常: {str(exc)}")
+    return JSONResponse(
+        status_code=500,
+        content={
+            "error": "Internal server error",
+            "message": str(exc) if config.debug else "Something went wrong"
+        }
+    )
+
+if __name__ == "__main__":
+    uvicorn.run(
+        "innocore_ai.api.main:app",
+        host="0.0.0.0",
+        port=8000,
+        reload=config.debug,
+        log_level="info"
+    )

+ 7 - 0
Co-creation-projects/Apricity-InnocoreAI/api/routes/__init__.py

@@ -0,0 +1,7 @@
+"""
+API路由模块
+"""
+
+from . import papers, users, tasks, analysis, writing, citations, workflow
+
+__all__ = ["papers", "users", "tasks", "analysis", "writing", "citations", "workflow"]

+ 567 - 0
Co-creation-projects/Apricity-InnocoreAI/api/routes/analysis.py

@@ -0,0 +1,567 @@
+"""
+分析相关API路由
+"""
+
+from fastapi import APIRouter, HTTPException, UploadFile, File
+from typing import Dict, Any, Optional, List
+from pydantic import BaseModel
+import logging
+import arxiv
+import os
+from core.config import get_config
+from core.llm_adapter import get_llm_adapter
+from utils.pdf_parser import pdf_parser
+
+logger = logging.getLogger(__name__)
+router = APIRouter()
+
+# 初始化 LLM 适配器(基于 HelloAgent)
+config = get_config()
+try:
+    llm = get_llm_adapter() if config.llm.api_key else None
+except Exception as e:
+    logger.warning(f"LLM 初始化失败: {str(e)}")
+    llm = None
+
+# Pydantic模型
+class AnalysisRequest(BaseModel):
+    paper_id: str
+    user_id: Optional[str] = None
+    analysis_type: str = "full"  # full, quick, innovation_only
+
+class ComparisonRequest(BaseModel):
+    paper_ids: List[str]
+    user_id: Optional[str] = None
+    comparison_aspects: List[str] = ["method", "results", "innovation"]
+
+class InnovationSearchRequest(BaseModel):
+    query: str
+    user_id: Optional[str] = None
+    search_scope: str = "both"  # l1, l2, both
+    top_k: int = 10
+
+class PaperAnalysisRequest(BaseModel):
+    paper_url: str
+    analysis_type: str = "summary"  # summary, innovation, comparison, comprehensive
+
+@router.post("/analyze", response_model=Dict[str, Any])
+async def analyze_paper(request: PaperAnalysisRequest):
+    """分析论文 - 支持 ArXiv URL 和本地 PDF 文件"""
+    try:
+        if not llm:
+            raise HTTPException(status_code=503, detail="AI 服务未配置,请设置 OPENAI_API_KEY")
+        
+        import re
+        paper_url = request.paper_url.strip()
+        
+        # 检查是否是本地上传的 PDF 文件
+        if paper_url.startswith('/uploads/') or paper_url.endswith('.pdf'):
+            logger.info(f"检测到本地 PDF 文件: {paper_url}")
+            
+            # 构建完整的文件路径
+            if paper_url.startswith('/uploads/'):
+                # 假设上传的文件在 downloads 目录
+                file_path = os.path.join('downloads', paper_url.replace('/uploads/', ''))
+            else:
+                file_path = paper_url
+            
+            # 检查文件是否存在
+            if not os.path.exists(file_path):
+                logger.warning(f"PDF 文件不存在: {file_path}")
+                raise HTTPException(status_code=404, detail=f"PDF 文件不存在: {paper_url}")
+            
+            # 解析 PDF 文件
+            logger.info(f"开始解析 PDF 文件: {file_path}")
+            pdf_result = await pdf_parser.parse_pdf(file_path)
+            
+            if not pdf_result.get("success"):
+                raise HTTPException(status_code=500, detail=pdf_result.get("error", "PDF 解析失败"))
+            
+            # 使用解析出的内容进行 AI 分析
+            title = pdf_result.get("title", "未知标题")
+            authors = pdf_result.get("authors", ["未知作者"])
+            abstract = pdf_result.get("abstract", "")
+            full_text = pdf_result.get("full_text", "")
+            
+            # 限制文本长度以避免超出 token 限制
+            text_for_analysis = full_text[:8000] if len(full_text) > 8000 else full_text
+            
+            # 根据分析类型生成提示词
+            prompts = {
+                "summary": f"""请对以下论文进行摘要分析:
+
+标题:{title}
+作者:{', '.join(authors)}
+摘要:{abstract}
+
+论文内容(前8000字符):
+{text_for_analysis}
+
+请提供:
+1. 研究背景和动机
+2. 主要方法
+3. 核心贡献
+4. 实验结果
+5. 研究意义
+
+请用中文回答,保持专业和简洁。""",
+                
+                "innovation": f"""请分析以下论文的创新点:
+
+标题:{title}
+摘要:{abstract}
+
+论文内容:
+{text_for_analysis}
+
+请详细分析:
+1. 技术创新点
+2. 方法论创新
+3. 理论贡献
+4. 与现有工作的区别
+5. 潜在应用价值
+
+请用中文回答。""",
+                
+                "comparison": f"""请对以下论文进行对比分析:
+
+标题:{title}
+摘要:{abstract}
+
+论文内容:
+{text_for_analysis}
+
+请分析:
+1. 与传统方法的对比
+2. 优势和劣势
+3. 适用场景
+4. 性能提升
+5. 局限性
+
+请用中文回答。""",
+                
+                "comprehensive": f"""请对以下论文进行全面综合分析:
+
+标题:{title}
+作者:{', '.join(authors)}
+摘要:{abstract}
+
+论文内容:
+{text_for_analysis}
+
+请提供全面的分析,包括:
+1. 研究背景和意义
+2. 技术方法详解
+3. 创新点分析
+4. 实验验证
+5. 优缺点评价
+6. 未来研究方向
+7. 实际应用价值
+
+请用中文回答,保持专业和深度。"""
+            }
+            
+            prompt = prompts.get(request.analysis_type, prompts["summary"])
+            
+            # 调用 LLM 进行分析
+            logger.info(f"开始 AI 分析,类型: {request.analysis_type}")
+            response = await llm.ainvoke(prompt)
+            analysis_content = response.content if hasattr(response, 'content') else str(response)
+            
+            return {
+                "success": True,
+                "paper_info": {
+                    "id": "local_pdf",
+                    "title": title,
+                    "authors": authors,
+                    "published_date": "N/A",
+                    "url": paper_url,
+                    "categories": ["本地文件"],
+                    "page_count": pdf_result.get("page_count", 0),
+                    "word_count": pdf_result.get("word_count", 0)
+                },
+                "analysis_type": request.analysis_type,
+                "analysis": analysis_content,
+                "abstract": abstract
+            }
+        
+        # ArXiv 论文处理
+        arxiv_patterns = [
+            r'arxiv\.org/abs/(\d+\.\d+)',
+            r'arxiv\.org/pdf/(\d+\.\d+)',
+            r'arXiv:(\d+\.\d+)',
+            r'\[(\d+\.\d+)v?\d*\]',
+            r'^(\d{4}\.\d{4,5})v?\d*$'
+        ]
+        
+        paper_id = None
+        for pattern in arxiv_patterns:
+            match = re.search(pattern, paper_url, re.IGNORECASE)
+            if match:
+                paper_id = match.group(1)
+                break
+        
+        if not paper_id:
+            raise HTTPException(
+                status_code=400, 
+                detail=f"无效的输入。支持的格式:\n" +
+                       "- ArXiv URL: https://arxiv.org/abs/2511.16672\n" +
+                       "- ArXiv ID: 2511.16672\n" +
+                       "- 本地 PDF: 上传后自动填充"
+            )
+        
+        logger.info(f"正在分析 ArXiv 论文: {paper_id}")
+        
+        # 获取论文信息
+        search = arxiv.Search(id_list=[paper_id])
+        paper = next(search.results(), None)
+        
+        if not paper:
+            raise HTTPException(status_code=404, detail=f"未找到 ArXiv 论文: {paper_id}")
+        
+        # 根据分析类型生成提示词
+        prompts = {
+            "summary": f"""请对以下论文进行摘要分析:
+
+标题:{paper.title}
+作者:{', '.join([a.name for a in paper.authors])}
+摘要:{paper.summary}
+
+请提供:
+1. 研究背景和动机
+2. 主要方法
+3. 核心贡献
+4. 实验结果
+5. 研究意义
+
+请用中文回答,保持专业和简洁。""",
+            
+            "innovation": f"""请分析以下论文的创新点:
+
+标题:{paper.title}
+摘要:{paper.summary}
+
+请详细分析:
+1. 技术创新点
+2. 方法论创新
+3. 理论贡献
+4. 与现有工作的区别
+5. 潜在应用价值
+
+请用中文回答。""",
+            
+            "comparison": f"""请对以下论文进行对比分析:
+
+标题:{paper.title}
+摘要:{paper.summary}
+
+请分析:
+1. 与传统方法的对比
+2. 优势和劣势
+3. 适用场景
+4. 性能提升
+5. 局限性
+
+请用中文回答。""",
+            
+            "comprehensive": f"""请对以下论文进行全面综合分析:
+
+标题:{paper.title}
+作者:{', '.join([a.name for a in paper.authors])}
+摘要:{paper.summary}
+分类:{', '.join(paper.categories)}
+
+请提供全面的分析,包括:
+1. 研究背景和意义
+2. 技术方法详解
+3. 创新点分析
+4. 实验验证
+5. 优缺点评价
+6. 未来研究方向
+7. 实际应用价值
+
+请用中文回答,保持专业和深度。"""
+        }
+        
+        prompt = prompts.get(request.analysis_type, prompts["summary"])
+        
+        # 调用 LLM 进行分析
+        response = await llm.ainvoke(prompt)
+        analysis_content = response.content if hasattr(response, 'content') else str(response)
+        
+        return {
+            "success": True,
+            "paper_info": {
+                "id": paper_id,
+                "title": paper.title,
+                "authors": [a.name for a in paper.authors],
+                "published_date": paper.published.strftime("%Y-%m-%d"),
+                "url": paper.entry_id,
+                "categories": paper.categories
+            },
+            "analysis_type": request.analysis_type,
+            "analysis": analysis_content,
+            "abstract": paper.summary
+        }
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"论文分析失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"分析失败: {str(e)}")
+
+@router.post("/compare", response_model=Dict[str, Any])
+async def compare_papers(request: ComparisonRequest):
+    """对比多篇论文"""
+    try:
+        # 这里需要实现论文对比逻辑
+        # 暂时返回模拟结果
+        
+        comparison_result = {
+            "paper_ids": request.paper_ids,
+            "comparison_aspects": request.comparison_aspects,
+            "similarities": ["相似点1", "相似点2"],
+            "differences": ["差异点1", "差异点2"],
+            "innovation_gaps": ["创新空白1", "创新空白2"],
+            "recommendations": ["建议1", "建议2"]
+        }
+        
+        return {
+            "success": True,
+            "result": comparison_result
+        }
+        
+    except Exception as e:
+        logger.error(f"论文对比失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/innovation/search", response_model=Dict[str, Any])
+async def search_innovation_opportunities(request: InnovationSearchRequest):
+    """搜索创新机会"""
+    try:
+        # 这里需要实现创新机会搜索逻辑
+        # 暂时返回模拟结果
+        
+        innovation_results = {
+            "query": request.query,
+            "opportunities": [
+                {
+                    "title": "创新机会1",
+                    "description": "基于当前研究的创新方向",
+                    "related_papers": ["paper1", "paper2"],
+                    "confidence": 0.85
+                },
+                {
+                    "title": "创新机会2", 
+                    "description": "另一个潜在的研究方向",
+                    "related_papers": ["paper3", "paper4"],
+                    "confidence": 0.72
+                }
+            ],
+            "research_gaps": ["研究空白1", "研究空白2"],
+            "future_directions": ["未来方向1", "未来方向2"]
+        }
+        
+        return {
+            "success": True,
+            "result": innovation_results
+        }
+        
+    except Exception as e:
+        logger.error(f"创新机会搜索失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/paper/{paper_id}/summary")
+async def get_paper_summary(paper_id: str, user_id: Optional[str] = None):
+    """获取论文摘要"""
+    try:
+        # 这里需要实现论文摘要生成逻辑
+        # 暂时返回模拟结果
+        
+        summary = {
+            "paper_id": paper_id,
+            "summary": "这是一篇关于...的论文,主要贡献包括...",
+            "key_contributions": ["贡献1", "贡献2", "贡献3"],
+            "methodology": "论文采用的方法是...",
+            "results": "实验结果表明...",
+            "limitations": "研究的局限性包括...",
+            "future_work": "未来工作方向..."
+        }
+        
+        return {
+            "success": True,
+            "summary": summary
+        }
+        
+    except Exception as e:
+        logger.error(f"获取论文摘要失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/paper/{paper_id}/innovations")
+async def get_paper_innovations(paper_id: str, user_id: Optional[str] = None):
+    """获取论文创新点"""
+    try:
+        # 这里需要实现创新点提取逻辑
+        # 暂时返回模拟结果
+        
+        innovations = {
+            "paper_id": paper_id,
+            "innovations": [
+                {
+                    "aspect": "方法创新",
+                    "description": "提出了新的方法...",
+                    "novelty": "high",
+                    "impact": "significant"
+                },
+                {
+                    "aspect": "理论创新", 
+                    "description": "在理论上有所突破...",
+                    "novelty": "medium",
+                    "impact": "moderate"
+                }
+            ],
+            "comparison_with_prior_work": "与之前的工作相比...",
+            "potential_applications": ["应用1", "应用2"]
+        }
+        
+        return {
+            "success": True,
+            "innovations": innovations
+        }
+        
+    except Exception as e:
+        logger.error(f"获取论文创新点失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/user/{user_id}/insights")
+async def get_user_insights(user_id: str):
+    """获取用户研究洞察"""
+    try:
+        # 这里需要实现用户研究洞察分析
+        # 暂时返回模拟结果
+        
+        insights = {
+            "user_id": user_id,
+            "research_interests": ["兴趣1", "兴趣2"],
+            "reading_patterns": {
+                "papers_read": 50,
+                "favorite_topics": ["主题1", "主题2"],
+                "reading_frequency": "daily"
+            },
+            "knowledge_gaps": ["知识空白1", "知识空白2"],
+            "research_suggestions": [
+                {
+                    "topic": "建议研究方向1",
+                    "reason": "基于您的阅读历史...",
+                    "related_papers": ["paper1", "paper2"]
+                }
+            ],
+            "skill_assessment": {
+                "technical_skills": ["技能1", "技能2"],
+                "writing_skills": ["写作技能1", "写作技能2"],
+                "improvement_areas": ["改进领域1", "改进领域2"]
+            }
+        }
+        
+        return {
+            "success": True,
+            "insights": insights
+        }
+        
+    except Exception as e:
+        logger.error(f"获取用户研究洞察失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/batch", response_model=Dict[str, Any])
+async def batch_analyze_papers(paper_ids: List[str], user_id: Optional[str] = None):
+    """批量分析论文"""
+    try:
+        results = []
+        
+        for paper_id in paper_ids:
+            try:
+                # 提交论文分析任务
+                task_id = await agent_controller.submit_task(
+                    TaskType.PAPER_ANALYSIS,
+                    {
+                        "paper_id": paper_id,
+                        "user_id": user_id,
+                        "analysis_type": "quick"  # 批量分析使用快速模式
+                    }
+                )
+                
+                # 执行任务
+                result = await agent_controller.execute_task(task_id)
+                
+                results.append({
+                    "paper_id": paper_id,
+                    "task_id": task_id,
+                    "success": True,
+                    "result": result
+                })
+                
+            except Exception as e:
+                results.append({
+                    "paper_id": paper_id,
+                    "success": False,
+                    "error": str(e)
+                })
+        
+        return {
+            "success": True,
+            "total_papers": len(paper_ids),
+            "successful_analyses": sum(1 for r in results if r["success"]),
+            "results": results
+        }
+        
+    except Exception as e:
+        logger.error(f"批量分析论文失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/upload-pdf", response_model=Dict[str, Any])
+async def upload_pdf_for_analysis(file: UploadFile = File(...)):
+    """
+    上传 PDF 文件并解析
+    返回文件信息和解析结果
+    """
+    try:
+        # 检查文件类型
+        if not file.filename.endswith('.pdf'):
+            raise HTTPException(status_code=400, detail="只支持 PDF 文件")
+        
+        # 读取文件内容
+        logger.info(f"接收到 PDF 文件: {file.filename}")
+        pdf_bytes = await file.read()
+        
+        # 解析 PDF
+        pdf_result = await pdf_parser.parse_pdf_from_bytes(pdf_bytes, file.filename)
+        
+        if not pdf_result.get("success"):
+            raise HTTPException(status_code=500, detail=pdf_result.get("error", "PDF 解析失败"))
+        
+        # 保存文件到 downloads 目录
+        os.makedirs("downloads", exist_ok=True)
+        file_path = os.path.join("downloads", file.filename)
+        
+        with open(file_path, "wb") as f:
+            f.write(pdf_bytes)
+        
+        logger.info(f"PDF 文件已保存: {file_path}")
+        
+        return {
+            "success": True,
+            "filename": file.filename,
+            "file_path": f"/uploads/{file.filename}",
+            "title": pdf_result.get("title", "未知标题"),
+            "authors": pdf_result.get("authors", ["未知作者"]),
+            "abstract": pdf_result.get("abstract", "")[:500],  # 限制摘要长度
+            "page_count": pdf_result.get("page_count", 0),
+            "word_count": pdf_result.get("word_count", 0),
+            "message": "PDF 文件上传并解析成功,可以使用返回的 file_path 进行分析"
+        }
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"PDF 上传失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")

+ 330 - 0
Co-creation-projects/Apricity-InnocoreAI/api/routes/citations.py

@@ -0,0 +1,330 @@
+"""
+引用校验API路由
+"""
+
+from fastapi import APIRouter, HTTPException
+from typing import Dict, Any, Optional
+from pydantic import BaseModel
+import logging
+import httpx
+import re
+
+logger = logging.getLogger(__name__)
+router = APIRouter()
+
+# Pydantic模型
+class CitationValidationRequest(BaseModel):
+    citation: str
+    format: str = "bibtex"  # bibtex, apa, ieee, mla
+
+class CitationGenerateRequest(BaseModel):
+    doi: Optional[str] = None
+    title: Optional[str] = None
+    authors: Optional[str] = None
+    year: Optional[int] = None
+    journal: Optional[str] = None
+    format: str = "bibtex"
+
+@router.post("/validate", response_model=Dict[str, Any])
+async def validate_citation(request: CitationValidationRequest):
+    """校验引用格式 - 支持 ArXiv、DOI 和 AI 辅助验证"""
+    try:
+        logger.info(f"校验引用: {request.citation[:100]}...")
+        
+        metadata = None
+        verified = False
+        doi = None
+        
+        # 1. 尝试识别 ArXiv URL 或 ID
+        arxiv_pattern = r'(?:arxiv\.org/abs/|arXiv:)(\d+\.\d+)'
+        arxiv_match = re.search(arxiv_pattern, request.citation, re.IGNORECASE)
+        
+        if arxiv_match:
+            arxiv_id = arxiv_match.group(1)
+            logger.info(f"找到 ArXiv ID: {arxiv_id}")
+            
+            try:
+                import arxiv
+                search = arxiv.Search(id_list=[arxiv_id])
+                paper = next(search.results(), None)
+                
+                if paper:
+                    metadata = {
+                        'title': paper.title,
+                        'authors': [author.name for author in paper.authors],
+                        'year': paper.published.year,
+                        'journal': 'arXiv preprint',
+                        'arxiv_id': arxiv_id,
+                        'url': paper.entry_id
+                    }
+                    verified = True
+                    logger.info(f"ArXiv 论文信息获取成功: {metadata['title'][:50]}...")
+                    logger.info(f"作者数量: {len(metadata['authors'])}")
+                else:
+                    logger.warning(f"未找到 ArXiv ID: {arxiv_id}")
+            except Exception as e:
+                logger.error(f"ArXiv 查询失败: {str(e)}", exc_info=True)
+        
+        # 2. 尝试从引用中提取 DOI
+        if not verified:
+            doi_pattern = r'10\.\d{4,9}/[-._;()/:A-Z0-9]+'
+            doi_match = re.search(doi_pattern, request.citation, re.IGNORECASE)
+            
+            if doi_match:
+                doi = doi_match.group(0)
+                logger.info(f"找到 DOI: {doi}")
+                
+                # 使用 Crossref API 验证 DOI
+                async with httpx.AsyncClient() as client:
+                    try:
+                        response = await client.get(
+                            f"https://api.crossref.org/works/{doi}",
+                            timeout=10.0
+                        )
+                        if response.status_code == 200:
+                            data = response.json()
+                            msg = data.get('message', {})
+                            metadata = {
+                                'title': msg.get('title', [''])[0],
+                                'authors': [f"{a.get('given', '')} {a.get('family', '')}" for a in msg.get('author', [])],
+                                'year': msg.get('published', {}).get('date-parts', [[None]])[0][0],
+                                'journal': msg.get('container-title', [''])[0],
+                                'volume': msg.get('volume', ''),
+                                'issue': msg.get('issue', ''),
+                                'pages': msg.get('page', ''),
+                                'doi': doi
+                            }
+                            verified = True
+                            logger.info("DOI 验证成功")
+                    except Exception as e:
+                        logger.warning(f"DOI 验证失败: {str(e)}")
+        
+        # 3. 如果仍未验证,尝试使用 AI 解析引用信息
+        if not verified:
+            logger.info("尝试使用 AI 解析引用信息...")
+            try:
+                from core.config import get_config
+                from core.llm_adapter import get_llm_adapter
+                
+                config = get_config()
+                if config.llm.api_key:
+                    llm = get_llm_adapter()
+                    
+                    prompt = f"""请从以下引用信息中提取关键元数据,并以 JSON 格式返回。
+
+引用信息:
+{request.citation}
+
+请提取以下信息(如果有的话):
+- title: 论文标题
+- authors: 作者列表(字符串数组,例如 ["Zhang San", "Li Si"])
+- year: 发表年份(数字)
+- journal: 期刊或会议名称
+- volume: 卷号
+- issue: 期号
+- pages: 页码
+- doi: DOI(如果有)
+- arxiv_id: ArXiv ID(如果有)
+
+只返回纯 JSON 格式,不要任何其他文字说明。如果某个字段不存在,请省略该字段。
+
+示例输出:
+{{"title": "论文标题", "authors": ["作者1", "作者2"], "year": 2024, "journal": "期刊名"}}"""
+                    
+                    response = await llm.ainvoke(prompt)
+                    ai_result = response.content if hasattr(response, 'content') else str(response)
+                    
+                    # 尝试解析 AI 返回的 JSON
+                    import json
+                    # 提取 JSON 部分(支持代码块格式)
+                    json_match = re.search(r'```(?:json)?\s*(\{[\s\S]*?\})\s*```', ai_result)
+                    if json_match:
+                        metadata = json.loads(json_match.group(1))
+                        verified = True
+                        logger.info("AI 解析成功(代码块格式)")
+                    else:
+                        json_match = re.search(r'\{[\s\S]*\}', ai_result)
+                        if json_match:
+                            metadata = json.loads(json_match.group(0))
+                            verified = True
+                            logger.info("AI 解析成功")
+            except Exception as e:
+                logger.warning(f"AI 解析失败: {str(e)}")
+        
+        # 生成标准格式的引用
+        if metadata and verified:
+            title = metadata.get('title', 'Unknown Title')
+            authors = metadata.get('authors', []) if isinstance(metadata.get('authors'), list) else [metadata.get('authors', 'Unknown Author')]
+            year = metadata.get('year', 'n.d.')
+            journal = metadata.get('journal', 'Unknown Journal')
+            volume = metadata.get('volume', '')
+            issue = metadata.get('issue', '')
+            pages = metadata.get('pages', '')
+            doi = metadata.get('doi', doi)
+            arxiv_id = metadata.get('arxiv_id', '')
+            
+            # 处理作者列表
+            if isinstance(authors, list):
+                if len(authors) > 3:
+                    author_str = ', '.join(authors[:3]) + ' et al.'
+                else:
+                    author_str = ', '.join(authors)
+            else:
+                author_str = str(authors)
+            
+            # 生成不同格式的引用
+            # BibTeX 格式
+            bibtex_parts = [
+                f"@article{{key{year},",
+                f"  title={{{title}}},",
+                f"  author={{{author_str}}},",
+                f"  journal={{{journal}}},",
+                f"  year={{{year}}}"
+            ]
+            if volume:
+                bibtex_parts.append(f"  volume={{{volume}}}")
+            if issue:
+                bibtex_parts.append(f"  number={{{issue}}}")
+            if pages:
+                bibtex_parts.append(f"  pages={{{pages}}}")
+            if arxiv_id:
+                bibtex_parts.append(f"  eprint={{{arxiv_id}}}")
+                bibtex_parts.append(f"  archivePrefix={{arXiv}}")
+            if doi:
+                bibtex_parts.append(f"  doi={{{doi}}}")
+            
+            bibtex_citation = ',\n'.join(bibtex_parts) + '\n}'
+            
+            # APA 格式
+            vol_str = f', {volume}' if volume else ''
+            issue_str = f'({issue})' if issue else ''
+            pages_str = f', {pages}' if pages else ''
+            
+            if arxiv_id:
+                apa_citation = f"{author_str} ({year}). {title}. *{journal}*{vol_str}{issue_str}{pages_str}. arXiv:{arxiv_id}"
+            elif doi:
+                apa_citation = f"{author_str} ({year}). {title}. *{journal}*{vol_str}{issue_str}{pages_str}. https://doi.org/{doi}"
+            else:
+                apa_citation = f"{author_str} ({year}). {title}. *{journal}*{vol_str}{issue_str}{pages_str}."
+            
+            # IEEE 格式
+            vol_ieee = f', vol. {volume}' if volume else ''
+            issue_ieee = f', no. {issue}' if issue else ''
+            pages_ieee = f', pp. {pages}' if pages else ''
+            
+            if arxiv_id:
+                ieee_citation = f'[1] {author_str}, "{title}," *{journal}*{vol_ieee}{issue_ieee}{pages_ieee}, {year}, arXiv:{arxiv_id}.'
+            elif doi:
+                ieee_citation = f'[1] {author_str}, "{title}," *{journal}*{vol_ieee}{issue_ieee}{pages_ieee}, {year}, doi: {doi}.'
+            else:
+                ieee_citation = f'[1] {author_str}, "{title}," *{journal}*{vol_ieee}{issue_ieee}{pages_ieee}, {year}.'
+            
+            vol_mla = f', vol. {volume}' if volume else ''
+            issue_mla = f', no. {issue}' if issue else ''
+            pages_mla = f', pp. {pages}' if pages else ''
+            
+            mla_citation = f'{author_str}. "{title}." *{journal}*{vol_mla}{issue_mla}, {year}{pages_mla}.'
+            
+            citations = {
+                "bibtex": bibtex_citation,
+                "apa": apa_citation,
+                "ieee": ieee_citation,
+                "mla": mla_citation
+            }
+            
+            formatted_citation = citations.get(request.format, citations["bibtex"])
+        else:
+            # 如果无法验证,返回原始引用和警告
+            formatted_citation = request.citation
+            verified = False
+        
+        result = {
+            "success": True,
+            "original_citation": request.citation,
+            "formatted_citation": formatted_citation,
+            "format": request.format,
+            "verified": verified,
+            "metadata": metadata if verified else None,
+            "warnings": [] if verified else ["无法自动验证引用,已返回原始格式。建议提供包含 DOI 的引用信息以获得更准确的结果。"]
+        }
+        
+        logger.info(f"返回结果 - verified: {verified}, metadata: {metadata is not None}")
+        if metadata:
+            logger.info(f"Metadata keys: {list(metadata.keys())}")
+        
+        return result
+        
+    except Exception as e:
+        logger.error(f"引用校验失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"校验失败: {str(e)}")
+
+@router.post("/generate", response_model=Dict[str, Any])
+async def generate_citation(request: CitationGenerateRequest):
+    """生成引用格式"""
+    try:
+        # 模拟引用生成
+        newline = "\n"
+        quote = '"'
+        citation_formats = {
+            "bibtex": f"@article{{{request.authors or 'author2024'},{newline}  title={{{request.title or 'Title'}}},{newline}  author={{{request.authors or 'Author'}}},{newline}  journal={{{request.journal or 'Journal'}}},{newline}  year={{{request.year or 2024}}}{newline}}}",
+            "apa": f"{request.authors or 'Author'} ({request.year or 2024}). {request.title or 'Title'}. *{request.journal or 'Journal'}*.",
+            "ieee": f"[1] {request.authors or 'Author'}, {quote}{request.title or 'Title'},{quote} *{request.journal or 'Journal'}*, {request.year or 2024}.",
+            "mla": f"{request.authors or 'Author'}. {quote}{request.title or 'Title'}.{quote} *{request.journal or 'Journal'}*, {request.year or 2024}."
+        }
+        
+        citation = citation_formats.get(request.format, citation_formats["bibtex"])
+        
+        return {
+            "success": True,
+            "citation": citation,
+            "format": request.format,
+            "metadata": {
+                "title": request.title,
+                "authors": request.authors,
+                "year": request.year,
+                "journal": request.journal,
+                "doi": request.doi
+            },
+            "timestamp": "2024-01-15T10:30:00Z"
+        }
+        
+    except Exception as e:
+        logger.error(f"引用生成失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"生成失败: {str(e)}")
+
+@router.get("/formats", response_model=Dict[str, Any])
+async def get_citation_formats():
+    """获取支持的引用格式"""
+    try:
+        formats = {
+            "bibtex": {
+                "name": "BibTeX",
+                "description": "常用于LaTeX文档的引用格式",
+                "example": "@article{key, title={Title}, author={Author}, year={2024}}"
+            },
+            "apa": {
+                "name": "APA",
+                "description": "美国心理学会格式,常用于社会科学",
+                "example": "Author, A. (2024). Title. *Journal*, 1(1), 1-10."
+            },
+            "ieee": {
+                "name": "IEEE",
+                "description": "电气电子工程师学会格式,常用于工程技术",
+                "example": "[1] A. Author, \"Title,\" *Journal*, vol. 1, no. 1, pp. 1-10, 2024."
+            },
+            "mla": {
+                "name": "MLA",
+                "description": "现代语言学会格式,常用于人文学科",
+                "example": "Author. \"Title.\" *Journal*, vol. 1, no. 1, 2024, pp. 1-10."
+            }
+        }
+        
+        return {
+            "success": True,
+            "formats": formats,
+            "total": len(formats)
+        }
+        
+    except Exception as e:
+        logger.error(f"获取引用格式失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"获取失败: {str(e)}")

+ 109 - 0
Co-creation-projects/Apricity-InnocoreAI/api/routes/papers.py

@@ -0,0 +1,109 @@
+"""
+论文相关API路由
+"""
+
+from fastapi import APIRouter, HTTPException, Depends, Query, UploadFile, File
+from typing import List, Optional, Dict, Any
+from pydantic import BaseModel
+import logging
+import arxiv
+from datetime import datetime
+
+logger = logging.getLogger(__name__)
+router = APIRouter()
+
+# Pydantic模型
+class PaperSearchRequest(BaseModel):
+    keywords: str
+    source: str = "arxiv"
+    limit: int = 10
+
+class PaperResponse(BaseModel):
+    id: str
+    title: str
+    authors: List[str]
+    abstract: str
+    url: str
+    published_date: str
+
+@router.post("/search", response_model=Dict[str, Any])
+async def search_papers(request: PaperSearchRequest):
+    """搜索论文 - 使用真实的 ArXiv API"""
+    try:
+        papers = []
+        
+        if request.source == "arxiv" or request.source == "all":
+            # 使用 ArXiv API 搜索
+            logger.info(f"正在搜索 ArXiv: {request.keywords}")
+            
+            # 构建搜索查询
+            search = arxiv.Search(
+                query=request.keywords,
+                max_results=request.limit,
+                sort_by=arxiv.SortCriterion.SubmittedDate,
+                sort_order=arxiv.SortOrder.Descending
+            )
+            
+            # 获取搜索结果
+            for result in search.results():
+                paper = {
+                    "id": result.entry_id.split('/')[-1],
+                    "title": result.title,
+                    "authors": [author.name for author in result.authors],
+                    "abstract": result.summary.replace('\n', ' ').strip(),
+                    "url": result.entry_id,
+                    "published_date": result.published.strftime("%Y-%m-%d"),
+                    "pdf_url": result.pdf_url,
+                    "categories": result.categories,
+                    "primary_category": result.primary_category
+                }
+                papers.append(paper)
+            
+            logger.info(f"找到 {len(papers)} 篇论文")
+        
+        # 如果没有找到结果,返回提示
+        if not papers:
+            return {
+                "success": True,
+                "papers": [],
+                "total_found": 0,
+                "keywords": request.keywords,
+                "source": request.source,
+                "message": "未找到相关论文,请尝试其他关键词"
+            }
+        
+        return {
+            "success": True,
+            "papers": papers,
+            "total_found": len(papers),
+            "keywords": request.keywords,
+            "source": request.source
+        }
+        
+    except Exception as e:
+        logger.error(f"论文搜索失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"搜索失败: {str(e)}")
+
+@router.post("/upload", response_model=Dict[str, Any])
+async def upload_paper(file: UploadFile = File(...)):
+    """上传论文PDF"""
+    try:
+        # 检查文件类型
+        if not file.filename.endswith('.pdf'):
+            raise HTTPException(status_code=400, detail="只支持PDF文件")
+        
+        # 模拟文件上传
+        file_url = f"/uploads/{file.filename}"
+        
+        return {
+            "success": True,
+            "file_url": file_url,
+            "filename": file.filename,
+            "size": getattr(file, 'size', 0),
+            "message": "文件上传成功"
+        }
+        
+    except Exception as e:
+        logger.error(f"文件上传失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")
+

+ 299 - 0
Co-creation-projects/Apricity-InnocoreAI/api/routes/tasks.py

@@ -0,0 +1,299 @@
+"""
+任务相关API路由
+"""
+
+from fastapi import APIRouter, HTTPException, WebSocket, WebSocketDisconnect
+from typing import List, Dict, Any, Optional
+from pydantic import BaseModel
+import logging
+import json
+import asyncio
+
+# from ...agents.controller import agent_controller, TaskType
+# 临时注释,避免相对导入错误
+agent_controller = None
+TaskType = None
+
+logger = logging.getLogger(__name__)
+router = APIRouter()
+
+# Pydantic模型
+class TaskSubmitRequest(BaseModel):
+    task_type: str
+    input_data: Dict[str, Any]
+    priority: int = 0
+
+class TaskResponse(BaseModel):
+    id: str
+    type: str
+    status: str
+    created_at: str
+    started_at: Optional[str]
+    completed_at: Optional[str]
+    priority: int
+
+# WebSocket连接管理
+class ConnectionManager:
+    def __init__(self):
+        self.active_connections: List[WebSocket] = []
+    
+    async def connect(self, websocket: WebSocket):
+        await websocket.accept()
+        self.active_connections.append(websocket)
+    
+    def disconnect(self, websocket: WebSocket):
+        self.active_connections.remove(websocket)
+    
+    async def send_personal_message(self, message: str, websocket: WebSocket):
+        await websocket.send_text(message)
+    
+    async def broadcast(self, message: str):
+        for connection in self.active_connections:
+            try:
+                await connection.send_text(message)
+            except:
+                # 连接已断开,移除
+                self.active_connections.remove(connection)
+
+manager = ConnectionManager()
+
+@router.post("/submit", response_model=Dict[str, Any])
+async def submit_task(request: TaskSubmitRequest):
+    """提交任务"""
+    try:
+        # 验证任务类型
+        try:
+            task_type = TaskType(request.task_type)
+        except ValueError:
+            raise HTTPException(status_code=400, detail=f"不支持的任务类型: {request.task_type}")
+        
+        # 提交任务
+        task_id = await agent_controller.submit_task(
+            task_type=task_type,
+            input_data=request.input_data,
+            priority=request.priority
+        )
+        
+        return {
+            "success": True,
+            "task_id": task_id,
+            "message": "任务已提交"
+        }
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"提交任务失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/{task_id}/execute", response_model=Dict[str, Any])
+async def execute_task(task_id: str):
+    """执行任务"""
+    try:
+        result = await agent_controller.execute_task(task_id)
+        
+        return {
+            "success": True,
+            "task_id": task_id,
+            "result": result
+        }
+        
+    except Exception as e:
+        logger.error(f"执行任务失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/{task_id}/status", response_model=TaskResponse)
+async def get_task_status(task_id: str):
+    """获取任务状态"""
+    try:
+        status = await agent_controller.get_task_status(task_id)
+        if not status:
+            raise HTTPException(status_code=404, detail="任务不存在")
+        
+        return TaskResponse(**status)
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"获取任务状态失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.delete("/{task_id}", response_model=Dict[str, Any])
+async def cancel_task(task_id: str):
+    """取消任务"""
+    try:
+        success = await agent_controller.cancel_task(task_id)
+        
+        if success:
+            return {"success": True, "message": "任务已取消"}
+        else:
+            return {"success": False, "message": "任务无法取消(可能正在执行或已完成)"}
+        
+    except Exception as e:
+        logger.error(f"取消任务失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/", response_model=List[TaskResponse])
+async def list_tasks():
+    """获取任务列表"""
+    try:
+        # 获取活跃任务
+        active_tasks = []
+        for task_id, task in agent_controller.active_tasks.items():
+            active_tasks.append(TaskResponse(
+                id=task["id"],
+                type=task["type"].value,
+                status=task["status"].value,
+                created_at=task["created_at"].isoformat(),
+                started_at=task["started_at"].isoformat() if task["started_at"] else None,
+                completed_at=task["completed_at"].isoformat() if task["completed_at"] else None,
+                priority=task["priority"]
+            ))
+        
+        # 获取历史任务(最近50个)
+        history_tasks = []
+        for task in agent_controller.task_history[-50:]:
+            history_tasks.append(TaskResponse(
+                id=task["id"],
+                type=task["type"].value,
+                status=task["status"].value,
+                created_at=task["created_at"].isoformat(),
+                started_at=task["started_at"].isoformat() if task["started_at"] else None,
+                completed_at=task["completed_at"].isoformat() if task["completed_at"] else None,
+                priority=task["priority"]
+            ))
+        
+        return active_tasks + history_tasks
+        
+    except Exception as e:
+        logger.error(f"获取任务列表失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/agents/status", response_model=Dict[str, Any])
+async def get_agents_status():
+    """获取智能体状态"""
+    try:
+        status = await agent_controller.get_agent_status()
+        return {
+            "success": True,
+            "agents": status
+        }
+        
+    except Exception as e:
+        logger.error(f"获取智能体状态失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/workflow/full", response_model=Dict[str, Any])
+async def run_full_workflow(input_data: Dict[str, Any]):
+    """运行完整工作流"""
+    try:
+        # 提交完整工作流任务
+        task_id = await agent_controller.submit_task(
+            task_type=TaskType.FULL_WORKFLOW,
+            input_data=input_data,
+            priority=1  # 高优先级
+        )
+        
+        # 执行任务
+        result = await agent_controller.execute_task(task_id)
+        
+        return {
+            "success": True,
+            "task_id": task_id,
+            "result": result
+        }
+        
+    except Exception as e:
+        logger.error(f"运行完整工作流失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.websocket("/ws/{task_id}")
+async def websocket_task_updates(websocket: WebSocket, task_id: str):
+    """WebSocket任务更新"""
+    await manager.connect(websocket)
+    try:
+        # 发送初始状态
+        status = await agent_controller.get_task_status(task_id)
+        if status:
+            await manager.send_personal_message(
+                json.dumps({"type": "status", "data": status}),
+                websocket
+            )
+        
+        # 监听任务状态变化
+        while True:
+            await asyncio.sleep(1)  # 每秒检查一次
+            
+            status = await agent_controller.get_task_status(task_id)
+            if status:
+                await manager.send_personal_message(
+                    json.dumps({"type": "status", "data": status}),
+                    websocket
+                )
+                
+                # 如果任务完成,断开连接
+                if status["status"] in ["completed", "failed", "cancelled"]:
+                    break
+    
+    except WebSocketDisconnect:
+        manager.disconnect(websocket)
+    except Exception as e:
+        logger.error(f"WebSocket连接异常: {str(e)}")
+        manager.disconnect(websocket)
+
+@router.websocket("/ws/stream")
+async def websocket_stream(websocket: WebSocket):
+    """WebSocket流式通信(用于写作助教等实时交互)"""
+    await manager.connect(websocket)
+    try:
+        while True:
+            # 接收消息
+            data = await websocket.receive_text()
+            message = json.loads(data)
+            
+            # 处理不同类型的消息
+            if message.get("type") == "writing_assistance":
+                # 处理写作辅助请求
+                await handle_writing_assistance(websocket, message.get("data", {}))
+            elif message.get("type") == "ping":
+                # 心跳检测
+                await manager.send_personal_message(
+                    json.dumps({"type": "pong"}),
+                    websocket
+                )
+    
+    except WebSocketDisconnect:
+        manager.disconnect(websocket)
+    except Exception as e:
+        logger.error(f"WebSocket流式通信异常: {str(e)}")
+        manager.disconnect(websocket)
+
+async def handle_writing_assistance(websocket: WebSocket, data: Dict[str, Any]):
+    """处理写作辅助请求"""
+    try:
+        # 提交写作辅助任务
+        task_id = await agent_controller.submit_task(
+            task_type=TaskType.WRITING_ASSISTANCE,
+            input_data=data
+        )
+        
+        # 发送任务ID
+        await manager.send_personal_message(
+            json.dumps({"type": "task_started", "task_id": task_id}),
+            websocket
+        )
+        
+        # 执行任务
+        result = await agent_controller.execute_task(task_id)
+        
+        # 发送结果
+        await manager.send_personal_message(
+            json.dumps({"type": "task_completed", "result": result}),
+            websocket
+        )
+        
+    except Exception as e:
+        await manager.send_personal_message(
+            json.dumps({"type": "error", "message": str(e)}),
+            websocket
+        )

+ 132 - 0
Co-creation-projects/Apricity-InnocoreAI/api/routes/users.py

@@ -0,0 +1,132 @@
+"""
+用户相关API路由
+"""
+
+from fastapi import APIRouter, HTTPException
+from typing import Dict, Any, Optional
+from pydantic import BaseModel
+import logging
+import uuid
+
+# from ...core.database import db_manager
+# 临时注释,避免相对导入错误
+db_manager = None
+
+logger = logging.getLogger(__name__)
+router = APIRouter()
+
+# Pydantic模型
+class UserCreateRequest(BaseModel):
+    email: str
+    profile: Optional[Dict[str, Any]] = {}
+
+class UserUpdateRequest(BaseModel):
+    profile: Optional[Dict[str, Any]] = {}
+
+class UserResponse(BaseModel):
+    id: str
+    email: str
+    profile: Dict[str, Any]
+    created_at: str
+
+@router.post("/", response_model=UserResponse)
+async def create_user(request: UserCreateRequest):
+    """创建用户"""
+    try:
+        user_id = await db_manager.create_user(
+            email=request.email,
+            profile=request.profile
+        )
+        
+        user = await db_manager.get_user(user_id)
+        
+        return UserResponse(
+            id=user["id"],
+            email=user["email"],
+            profile=user["profile"],
+            created_at=user["created_at"].isoformat() if user["created_at"] else ""
+        )
+        
+    except Exception as e:
+        logger.error(f"创建用户失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/{user_id}", response_model=UserResponse)
+async def get_user(user_id: str):
+    """获取用户信息"""
+    try:
+        user = await db_manager.get_user(user_id)
+        if not user:
+            raise HTTPException(status_code=404, detail="用户不存在")
+        
+        return UserResponse(
+            id=user["id"],
+            email=user["email"],
+            profile=user["profile"],
+            created_at=user["created_at"].isoformat() if user["created_at"] else ""
+        )
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"获取用户信息失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.put("/{user_id}", response_model=Dict[str, Any])
+async def update_user(user_id: str, request: UserUpdateRequest):
+    """更新用户信息"""
+    try:
+        success = await db_manager.update_user_profile(
+            user_id=user_id,
+            profile=request.profile
+        )
+        
+        if success:
+            return {"success": True, "message": "用户信息已更新"}
+        else:
+            raise HTTPException(status_code=404, detail="用户不存在")
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"更新用户信息失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/{user_id}/profile")
+async def get_user_profile(user_id: str):
+    """获取用户配置"""
+    try:
+        user = await db_manager.get_user(user_id)
+        if not user:
+            raise HTTPException(status_code=404, detail="用户不存在")
+        
+        return {
+            "success": True,
+            "profile": user.get("profile", {})
+        }
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"获取用户配置失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/{user_id}/profile")
+async def update_user_profile(user_id: str, profile: Dict[str, Any]):
+    """更新用户配置"""
+    try:
+        success = await db_manager.update_user_profile(
+            user_id=user_id,
+            profile=profile
+        )
+        
+        if success:
+            return {"success": True, "message": "用户配置已更新"}
+        else:
+            raise HTTPException(status_code=404, detail="用户不存在")
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"更新用户配置失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))

+ 304 - 0
Co-creation-projects/Apricity-InnocoreAI/api/routes/workflow.py

@@ -0,0 +1,304 @@
+"""
+工作流API路由 - 协调多个智能体完成复杂任务
+"""
+
+from fastapi import APIRouter, HTTPException
+from typing import Dict, Any, Optional, List
+from pydantic import BaseModel
+import logging
+import asyncio
+from agents.controller import agent_controller
+
+logger = logging.getLogger(__name__)
+router = APIRouter()
+
+# Pydantic模型
+class WorkflowRequest(BaseModel):
+    keywords: str
+    analysis_type: str = "summary"  # summary, innovation, comparison, comprehensive
+    citation_format: str = "bibtex"  # bibtex, apa, ieee, mla
+    writing_task: Optional[str] = None  # improve, polish, translate
+    limit: int = 5  # 搜索论文数量
+
+class WorkflowStatus(BaseModel):
+    workflow_id: str
+    status: str  # running, completed, failed
+    current_step: str
+    progress: int  # 0-100
+
+@router.post("/complete", response_model=Dict[str, Any])
+async def complete_workflow(request: WorkflowRequest):
+    """
+    完整工作流:搜索 -> 分析 -> 校验引用 -> 写作辅助
+    自动协调所有智能体完成任务
+    """
+    try:
+        workflow_id = f"workflow_{asyncio.get_event_loop().time()}"
+        results = {
+            "workflow_id": workflow_id,
+            "status": "running",
+            "steps": []
+        }
+        
+        # 步骤 1: Hunter - 搜索论文
+        logger.info(f"[工作流 {workflow_id}] 步骤 1/4: 搜索论文")
+        try:
+            from api.routes.papers import search_papers, PaperSearchRequest
+            
+            search_result = await search_papers(PaperSearchRequest(
+                keywords=request.keywords,
+                source="arxiv",
+                limit=request.limit
+            ))
+            
+            papers = search_result.get("papers", [])
+            results["steps"].append({
+                "step": 1,
+                "name": "Hunter - 论文搜索",
+                "status": "completed",
+                "result": {
+                    "total_found": len(papers),
+                    "papers": papers
+                }
+            })
+            
+            if not papers:
+                raise HTTPException(status_code=404, detail="未找到相关论文")
+            
+        except Exception as e:
+            logger.error(f"论文搜索失败: {str(e)}")
+            results["steps"].append({
+                "step": 1,
+                "name": "Hunter - 论文搜索",
+                "status": "failed",
+                "error": str(e)
+            })
+            results["status"] = "failed"
+            return results
+        
+        # 步骤 2: Miner - 分析每篇论文
+        logger.info(f"[工作流 {workflow_id}] 步骤 2/4: 分析论文")
+        analyses = []
+        try:
+            from api.routes.analysis import analyze_paper, PaperAnalysisRequest
+            
+            # 分析前3篇论文
+            for i, paper in enumerate(papers[:3]):
+                try:
+                    analysis_result = await analyze_paper(PaperAnalysisRequest(
+                        paper_url=paper["url"],
+                        analysis_type=request.analysis_type
+                    ))
+                    analyses.append({
+                        "paper_id": paper["id"],
+                        "title": paper["title"],
+                        "analysis": analysis_result.get("analysis", "")
+                    })
+                except Exception as e:
+                    logger.warning(f"分析论文 {paper['id']} 失败: {str(e)}")
+                    continue
+            
+            results["steps"].append({
+                "step": 2,
+                "name": "Miner - 论文分析",
+                "status": "completed",
+                "result": {
+                    "total_analyzed": len(analyses),
+                    "analyses": analyses
+                }
+            })
+            
+        except Exception as e:
+            logger.error(f"论文分析失败: {str(e)}")
+            results["steps"].append({
+                "step": 2,
+                "name": "Miner - 论文分析",
+                "status": "failed",
+                "error": str(e)
+            })
+        
+        # 步骤 3: Validator - 生成和校验引用
+        logger.info(f"[工作流 {workflow_id}] 步骤 3/4: 生成引用")
+        citations = []
+        try:
+            from api.routes.citations import validate_citation, CitationValidationRequest
+            
+            # 为每篇论文生成引用
+            for paper in papers[:3]:
+                try:
+                    # 构建引用文本
+                    authors_str = ", ".join(paper["authors"][:3])
+                    if len(paper["authors"]) > 3:
+                        authors_str += " et al."
+                    
+                    citation_text = f"{authors_str} ({paper['published_date'][:4]}). {paper['title']}. arXiv:{paper['id']}"
+                    
+                    citation_result = await validate_citation(CitationValidationRequest(
+                        citation=citation_text,
+                        format=request.citation_format
+                    ))
+                    
+                    citations.append({
+                        "paper_id": paper["id"],
+                        "title": paper["title"],
+                        "formatted_citation": citation_result.get("formatted_citation", "")
+                    })
+                except Exception as e:
+                    logger.warning(f"生成引用失败: {str(e)}")
+                    continue
+            
+            results["steps"].append({
+                "step": 3,
+                "name": "Validator - 引用生成",
+                "status": "completed",
+                "result": {
+                    "total_citations": len(citations),
+                    "citations": citations
+                }
+            })
+            
+        except Exception as e:
+            logger.error(f"引用生成失败: {str(e)}")
+            results["steps"].append({
+                "step": 3,
+                "name": "Validator - 引用生成",
+                "status": "failed",
+                "error": str(e)
+            })
+        
+        # 步骤 4: Coach - 生成综合报告(可选)
+        if request.writing_task:
+            logger.info(f"[工作流 {workflow_id}] 步骤 4/4: 生成报告")
+            try:
+                from api.routes.writing import writing_coach, WritingCoachRequest
+                
+                # 构建综合报告文本
+                report_text = f"# 关于 '{request.keywords}' 的研究综述\n\n"
+                report_text += f"## 搜索结果\n找到 {len(papers)} 篇相关论文\n\n"
+                
+                if analyses:
+                    report_text += "## 论文分析\n"
+                    for i, analysis in enumerate(analyses[:3], 1):
+                        report_text += f"\n### {i}. {analysis['title']}\n"
+                        report_text += f"{analysis['analysis'][:500]}...\n"
+                
+                if citations:
+                    report_text += "\n## 参考文献\n"
+                    for i, citation in enumerate(citations, 1):
+                        report_text += f"{i}. {citation['formatted_citation']}\n"
+                
+                # 使用 Coach 改进报告
+                writing_result = await writing_coach(WritingCoachRequest(
+                    text=report_text,
+                    style="academic",
+                    task=request.writing_task
+                ))
+                
+                results["steps"].append({
+                    "step": 4,
+                    "name": "Coach - 报告生成",
+                    "status": "completed",
+                    "result": {
+                        "report": writing_result.get("result", "")
+                    }
+                })
+                
+            except Exception as e:
+                logger.error(f"报告生成失败: {str(e)}")
+                results["steps"].append({
+                    "step": 4,
+                    "name": "Coach - 报告生成",
+                    "status": "failed",
+                    "error": str(e)
+                })
+        
+        # 完成工作流
+        results["status"] = "completed"
+        results["summary"] = {
+            "total_papers": len(papers),
+            "analyzed_papers": len(analyses),
+            "generated_citations": len(citations),
+            "keywords": request.keywords
+        }
+        
+        logger.info(f"[工作流 {workflow_id}] 完成")
+        return results
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"工作流执行失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"工作流执行失败: {str(e)}")
+
+@router.post("/search-and-analyze", response_model=Dict[str, Any])
+async def search_and_analyze(request: WorkflowRequest):
+    """
+    简化工作流:搜索 + 分析
+    只执行搜索和分析步骤
+    """
+    try:
+        results = {
+            "status": "running",
+            "steps": []
+        }
+        
+        # 步骤 1: 搜索论文
+        from api.routes.papers import search_papers, PaperSearchRequest
+        
+        search_result = await search_papers(PaperSearchRequest(
+            keywords=request.keywords,
+            source="arxiv",
+            limit=request.limit
+        ))
+        
+        papers = search_result.get("papers", [])
+        results["steps"].append({
+            "step": 1,
+            "name": "搜索论文",
+            "status": "completed",
+            "papers": papers
+        })
+        
+        if not papers:
+            raise HTTPException(status_code=404, detail="未找到相关论文")
+        
+        # 步骤 2: 分析第一篇论文
+        from api.routes.analysis import analyze_paper, PaperAnalysisRequest
+        
+        first_paper = papers[0]
+        analysis_result = await analyze_paper(PaperAnalysisRequest(
+            paper_url=first_paper["url"],
+            analysis_type=request.analysis_type
+        ))
+        
+        results["steps"].append({
+            "step": 2,
+            "name": "分析论文",
+            "status": "completed",
+            "analysis": analysis_result
+        })
+        
+        results["status"] = "completed"
+        return results
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"搜索和分析失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"执行失败: {str(e)}")
+
+@router.get("/status/{workflow_id}")
+async def get_workflow_status(workflow_id: str):
+    """获取工作流状态"""
+    try:
+        # 这里可以实现工作流状态跟踪
+        # 暂时返回模拟状态
+        return {
+            "workflow_id": workflow_id,
+            "status": "completed",
+            "progress": 100,
+            "message": "工作流已完成"
+        }
+    except Exception as e:
+        logger.error(f"获取工作流状态失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))

+ 383 - 0
Co-creation-projects/Apricity-InnocoreAI/api/routes/writing.py

@@ -0,0 +1,383 @@
+"""
+写作辅助API路由
+"""
+
+from fastapi import APIRouter, HTTPException
+from typing import Dict, Any, Optional
+from pydantic import BaseModel
+import logging
+from core.config import get_config
+from core.llm_adapter import get_llm_adapter
+
+logger = logging.getLogger(__name__)
+router = APIRouter()
+
+# 初始化 LLM 适配器(基于 HelloAgent)
+config = get_config()
+try:
+    llm = get_llm_adapter() if config.llm.api_key else None
+except Exception as e:
+    logger.warning(f"LLM 初始化失败: {str(e)}")
+    llm = None
+
+# Pydantic模型
+class WritingAssistanceRequest(BaseModel):
+    user_id: str
+    task_type: str  # explain, polish, mimic, suggest
+    content: str
+    context: Optional[Dict[str, Any]] = {}
+
+class ExplainRequest(BaseModel):
+    user_id: str
+    concept: str
+    context: Optional[Dict[str, Any]] = {}
+
+class PolishRequest(BaseModel):
+    user_id: str
+    text: str
+    target_style: Optional[str] = "academic"
+
+class WritingCoachRequest(BaseModel):
+    text: str
+    style: str = "formal"
+    task: str = "polish"  # polish, translate, explain, expand
+    context: Optional[Dict[str, Any]] = {}
+
+class MimicRequest(BaseModel):
+    user_id: str
+    text: str
+    target_style: str
+    reference_papers: Optional[list] = []
+    context: Optional[Dict[str, Any]] = {}
+
+class SuggestRequest(BaseModel):
+    user_id: str
+    text: str
+    context: Optional[Dict[str, Any]] = {}
+
+@router.post("/coach", response_model=Dict[str, Any])
+async def writing_coach(request: WritingCoachRequest):
+    """写作助手 - 使用真实的 AI 处理"""
+    try:
+        if not llm:
+            raise HTTPException(status_code=503, detail="AI 服务未配置,请设置 OPENAI_API_KEY")
+        
+        logger.info(f"处理写作任务: {request.task}, 风格: {request.style}")
+        
+        # 根据任务类型生成提示词
+        prompts = {
+            "polish": f"""作为一位专业的学术写作编辑,请帮我润色以下文本,使其符合{request.style}学术写作标准:
+
+原文:
+{request.text}
+
+请提供:
+1. 润色后的文本(保持原意,提升表达质量)
+2. 具体的改进说明
+3. 写作建议
+
+要求:
+- 保持学术严谨性
+- 提升表达清晰度
+- 使用恰当的学术用语
+- 改善句子结构和逻辑流畅性""",
+            
+            "translate": f"""请将以下中文学术文本翻译成专业的英文学术论文表达:
+
+原文:
+{request.text}
+
+要求:
+- 保持学术专业性和准确性
+- 使用地道的英文学术表达
+- 符合{request.style}风格的学术写作规范
+- 保持技术术语的准确性""",
+            
+            "explain": f"""请详细解释以下概念或内容:
+
+{request.text}
+
+要求:
+- 用通俗易懂的语言解释
+- 保持技术准确性
+- 提供具体例子
+- 说明应用场景和重要性""",
+            
+            "expand": f"""请扩展以下内容,使其更加详细和完整:
+
+原文:
+{request.text}
+
+要求:
+- 添加必要的背景信息
+- 补充相关的理论支持
+- 扩展方法论描述
+- 增加潜在影响和应用
+- 保持逻辑连贯性
+- 符合{request.style}学术写作风格"""
+        }
+        
+        prompt = prompts.get(request.task, prompts["polish"])
+        
+        # 调用 LLM 处理
+        response = await llm.ainvoke(prompt)
+        result_content = response.content if hasattr(response, 'content') else str(response)
+        
+        return {
+            "success": True,
+            "task": request.task,
+            "style": request.style,
+            "original": request.text,
+            "result": result_content
+        }
+        
+    except HTTPException:
+        raise
+    except Exception as e:
+        logger.error(f"写作助手处理失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=f"处理失败: {str(e)}")
+
+@router.post("/explain", response_model=Dict[str, Any])
+async def explain_concept(request: ExplainRequest):
+    """解释复杂概念"""
+    try:
+        # 模拟概念解释
+        return {
+            "success": True,
+            "concept": request.concept,
+            "explanation": f"[Detailed explanation of {request.concept} in accessible terms while maintaining technical accuracy]",
+            "examples": ["Example 1", "Example 2"],
+            "timestamp": "2024-01-15T10:30:00Z"
+        }
+        
+    except Exception as e:
+        logger.error(f"概念解释失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/polish", response_model=Dict[str, Any])
+async def polish_text(request: PolishRequest):
+    """润色文本"""
+    try:
+        # 模拟文本润色
+        return {
+            "success": True,
+            "original": request.text,
+            "improved": f"Based on {request.target_style} writing standards, the text can be improved: [Enhanced version]",
+            "suggestions": ["Use more precise terminology", "Improve sentence structure"],
+            "timestamp": "2024-01-15T10:30:00Z"
+        }
+        
+    except Exception as e:
+        logger.error(f"文本润色失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/mimic", response_model=Dict[str, Any])
+async def mimic_style(request: MimicRequest):
+    """模仿写作风格"""
+    try:
+        # 模拟风格模仿
+        return {
+            "success": True,
+            "original": request.text,
+            "mimicked": f"[Text rewritten in {request.target_style} style]",
+            "style_analysis": f"Analysis of {request.target_style} writing characteristics",
+            "timestamp": "2024-01-15T10:30:00Z"
+        }
+        
+    except Exception as e:
+        logger.error(f"风格模仿失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/suggest", response_model=Dict[str, Any])
+async def suggest_improvements(request: SuggestRequest):
+    """建议改进"""
+    try:
+        # 模拟改进建议
+        return {
+            "success": True,
+            "original": request.text,
+            "suggestions": [
+                "Consider adding more specific examples",
+                "Strengthen the introduction",
+                "Include recent citations",
+                "Clarify the methodology"
+            ],
+            "improved_version": "[Improved version with suggestions applied]",
+            "timestamp": "2024-01-15T10:30:00Z"
+        }
+        
+    except Exception as e:
+        logger.error(f"改进建议失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/user/{user_id}/style")
+async def get_user_writing_style(user_id: str):
+    """获取用户写作风格"""
+    try:
+        # 这里需要实现用户写作风格分析
+        # 暂时返回模拟结果
+        
+        style_profile = {
+            "user_id": user_id,
+            "writing_style": {
+                "tone": "formal_academic",
+                "complexity": "medium",
+                "sentence_length": "medium",
+                "vocabulary_richness": "high",
+                "clarity": "good"
+            },
+            "preferred_patterns": [
+                "句式模式1",
+                "句式模式2"
+            ],
+            "common_phrases": [
+                "常用短语1",
+                "常用短语2"
+            ],
+            "improvement_areas": [
+                "改进领域1",
+                "改进领域2"
+            ],
+            "style_evolution": {
+                "last_month": "上个月的风格变化",
+                "trend": "improving"
+            }
+        }
+        
+        return {
+            "success": True,
+            "style_profile": style_profile
+        }
+        
+    except Exception as e:
+        logger.error(f"获取用户写作风格失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.get("/user/{user_id}/templates")
+async def get_writing_templates(user_id: str):
+    """获取写作模板"""
+    try:
+        # 这里需要实现写作模板推荐
+        # 暂时返回模拟结果
+        
+        templates = {
+            "user_id": user_id,
+            "templates": [
+                {
+                    "id": "abstract_template",
+                    "name": "摘要模板",
+                    "category": "academic",
+                    "structure": [
+                        "背景介绍",
+                        "问题陈述", 
+                        "方法概述",
+                        "主要结果",
+                        "结论意义"
+                    ],
+                    "example": "摘要示例...",
+                    "usage_count": 15
+                },
+                {
+                    "id": "introduction_template",
+                    "name": "引言模板",
+                    "category": "academic",
+                    "structure": [
+                        "研究背景",
+                        "相关工作",
+                        "研究空白",
+                        "主要贡献",
+                        "论文结构"
+                    ],
+                    "example": "引言示例...",
+                    "usage_count": 8
+                }
+            ],
+            "recommended_templates": [
+                "推荐模板1",
+                "推荐模板2"
+            ]
+        }
+        
+        return {
+            "success": True,
+            "templates": templates
+        }
+        
+    except Exception as e:
+        logger.error(f"获取写作模板失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/check/grammar", response_model=Dict[str, Any])
+async def check_grammar(text: str, user_id: Optional[str] = None):
+    """语法检查"""
+    try:
+        # 这里需要实现语法检查逻辑
+        # 暂时返回模拟结果
+        
+        grammar_check = {
+            "text": text,
+            "errors": [
+                {
+                    "type": "grammar",
+                    "message": "语法错误描述",
+                    "position": {"start": 10, "end": 20},
+                    "suggestion": "修改建议",
+                    "severity": "medium"
+                }
+            ],
+            "suggestions": [
+                {
+                    "type": "style",
+                    "message": "风格建议",
+                    "position": {"start": 30, "end": 40},
+                    "suggestion": "风格改进建议"
+                }
+            ],
+            "score": 85,
+            "corrected_text": "修正后的文本..."
+        }
+        
+        return {
+            "success": True,
+            "grammar_check": grammar_check
+        }
+        
+    except Exception as e:
+        logger.error(f"语法检查失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))
+
+@router.post("/check/plagiarism", response_model=Dict[str, Any])
+async def check_plagiarism(text: str, user_id: Optional[str] = None):
+    """抄袭检查"""
+    try:
+        # 这里需要实现抄袭检查逻辑
+        # 暂时返回模拟结果
+        
+        plagiarism_check = {
+            "text": text,
+            "similarity_score": 15.5,
+            "sources": [
+                {
+                    "title": "相似文献标题",
+                    "authors": ["作者1", "作者2"],
+                    "similarity": 12.3,
+                    "matched_text": "匹配的文本片段...",
+                    "url": "文献链接"
+                }
+            ],
+            "originality_score": 84.5,
+            "risk_level": "low",
+            "recommendations": [
+                "建议1",
+                "建议2"
+            ]
+        }
+        
+        return {
+            "success": True,
+            "plagiarism_check": plagiarism_check
+        }
+        
+    except Exception as e:
+        logger.error(f"抄袭检查失败: {str(e)}")
+        raise HTTPException(status_code=500, detail=str(e))

+ 16 - 0
Co-creation-projects/Apricity-InnocoreAI/core/__init__.py

@@ -0,0 +1,16 @@
+"""
+InnoCore AI 核心模块
+"""
+
+from .config import InnoCoreConfig, get_config, update_config
+from .database import DatabaseManager
+from .vector_store import VectorStoreManager
+from .exceptions import *
+
+__all__ = [
+    "InnoCoreConfig",
+    "get_config", 
+    "update_config",
+    "DatabaseManager",
+    "VectorStoreManager"
+]

+ 153 - 0
Co-creation-projects/Apricity-InnocoreAI/core/config.py

@@ -0,0 +1,153 @@
+"""
+InnoCore AI 核心配置模块
+"""
+
+from typing import Dict, List, Optional, Any
+from dataclasses import dataclass, field
+from enum import Enum
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+class LLMProvider(Enum):
+    """LLM提供商枚举"""
+    OPENAI = "openai"
+    CLAUDE = "claude"
+    MODELSCOPE = "modelscope"  # 阿里云 ModelScope
+    OLLAMA = "ollama"  # 本地部署
+    DASHSCOPE = "dashscope"  # 阿里云灵积(推荐用于 Qwen 系列)
+
+class VectorDBType(Enum):
+    """向量数据库类型枚举"""
+    QDRANT = "qdrant"
+    CHROMA = "chroma"
+    PINECONE = "pinecone"
+
+@dataclass
+class LLMConfig:
+    """LLM配置"""
+    provider: LLMProvider = LLMProvider.OPENAI
+    model_name: str = "gpt-3.5-turbo"  # OpenAI: gpt-4, gpt-3.5-turbo, gpt-4-turbo-preview
+                                        # DashScope: qwen-turbo, qwen-plus, qwen-max
+                                        # ModelScope: qwen/Qwen2.5-7B-Instruct
+    api_key: Optional[str] = None
+    base_url: Optional[str] = None
+    temperature: float = 0.7
+    max_tokens: int = 4000
+    timeout: int = 60
+
+@dataclass
+class VectorDBConfig:
+    """向量数据库配置"""
+    db_type: VectorDBType = VectorDBType.QDRANT
+    host: str = "localhost"
+    port: int = 6333
+    api_key: Optional[str] = None
+    collection_name_prefix: str = "innocore"
+    embedding_model: str = "text-embedding-3-small"
+
+@dataclass
+class DatabaseConfig:
+    """关系数据库配置"""
+    host: str = "localhost"
+    port: int = 5432
+    database: str = "innocore_ai"
+    username: str = "postgres"
+    password: str = "password"
+    pool_size: int = 10
+
+@dataclass
+class RedisConfig:
+    """Redis配置"""
+    host: str = "localhost"
+    port: int = 6379
+    db: int = 0
+    password: Optional[str] = None
+    max_connections: int = 20
+
+@dataclass
+class ExternalAPIConfig:
+    """外部API配置"""
+    crossref_api_key: Optional[str] = None
+    google_scholar_api_key: Optional[str] = None
+    serpapi_key: Optional[str] = None
+    arxiv_base_url: str = "http://export.arxiv.org/api/query"
+    ieee_base_url: str = "https://ieeexploreapi.ieee.org/api/v1"
+
+@dataclass
+class InnoCoreConfig:
+    """InnoCore AI 主配置类"""
+    
+    # 基础配置
+    app_name: str = "InnoCore AI"
+    debug: bool = False
+    log_level: str = "INFO"
+    
+    # LLM配置
+    llm: LLMConfig = field(default_factory=LLMConfig)
+    
+    # 向量数据库配置
+    vector_db: VectorDBConfig = field(default_factory=VectorDBConfig)
+    
+    # 关系数据库配置
+    database: DatabaseConfig = field(default_factory=DatabaseConfig)
+    
+    # Redis配置
+    redis: RedisConfig = field(default_factory=RedisConfig)
+    
+    # 外部API配置
+    external_apis: ExternalAPIConfig = field(default_factory=ExternalAPIConfig)
+    
+    # Agent配置
+    agent_max_steps: int = 5
+    agent_timeout: int = 300
+    concurrent_agents: int = 4
+    
+    # RAG配置
+    retrieval_top_k: int = 5
+    similarity_threshold: float = 0.7
+    hybrid_search_weights: Dict[str, float] = field(default_factory=lambda: {
+        "vector": 0.7,
+        "keyword": 0.3
+    })
+    
+    # 性能配置
+    cache_ttl: int = 3600  # 缓存过期时间(秒)
+    batch_size: int = 10
+    max_concurrent_requests: int = 50
+    
+    def __post_init__(self):
+        """初始化后处理"""
+        # 从环境变量加载配置
+        self.llm.api_key = self.llm.api_key or os.getenv("OPENAI_API_KEY")
+        self.llm.base_url = self.llm.base_url or os.getenv("OPENAI_BASE_URL")
+        
+        # 从环境变量加载模型名称(如果设置了)
+        env_model = os.getenv("OPENAI_MODEL") or os.getenv("LLM_MODEL")
+        if env_model:
+            self.llm.model_name = env_model
+        
+        self.database.password = self.database.password or os.getenv("DATABASE_PASSWORD")
+        self.redis.password = self.redis.password or os.getenv("REDIS_PASSWORD")
+        
+        self.external_apis.crossref_api_key = self.external_apis.crossref_api_key or os.getenv("CROSSREF_API_KEY")
+        self.external_apis.google_scholar_api_key = self.external_apis.google_scholar_api_key or os.getenv("GOOGLE_SCHOLAR_API_KEY")
+        self.external_apis.serpapi_key = self.external_apis.serpapi_key or os.getenv("SERPAPI_KEY")
+        
+        self.debug = os.getenv("DEBUG", "false").lower() == "true"
+        self.log_level = os.getenv("LOG_LEVEL", "INFO")
+
+# 全局配置实例
+config = InnoCoreConfig()
+
+def get_config() -> InnoCoreConfig:
+    """获取全局配置实例"""
+    return config
+
+def update_config(**kwargs) -> None:
+    """更新配置"""
+    global config
+    for key, value in kwargs.items():
+        if hasattr(config, key):
+            setattr(config, key, value)

+ 303 - 0
Co-creation-projects/Apricity-InnocoreAI/core/database.py

@@ -0,0 +1,303 @@
+"""
+InnoCore AI 数据库管理模块
+"""
+
+import asyncio
+import asyncpg
+from typing import Dict, List, Optional, Any, Union
+from datetime import datetime
+import json
+import uuid
+from contextlib import asynccontextmanager
+
+from .config import get_config
+from .exceptions import DatabaseException
+
+class DatabaseManager:
+    """数据库管理器"""
+    
+    def __init__(self):
+        self.config = get_config().database
+        self.pool = None
+    
+    async def initialize(self):
+        """初始化数据库连接池"""
+        try:
+            self.pool = await asyncpg.create_pool(
+                host=self.config.host,
+                port=self.config.port,
+                database=self.config.database,
+                user=self.config.username,
+                password=self.config.password,
+                min_size=1,
+                max_size=self.config.pool_size
+            )
+            await self._create_tables()
+        except Exception as e:
+            raise DatabaseException(f"数据库初始化失败: {str(e)}")
+    
+    async def _create_tables(self):
+        """创建数据库表"""
+        create_tables_sql = """
+        -- 用户表
+        CREATE TABLE IF NOT EXISTS users (
+            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+            email VARCHAR(255) UNIQUE NOT NULL,
+            profile JSONB DEFAULT '{}',
+            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+        );
+        
+        -- 论文表
+        CREATE TABLE IF NOT EXISTS papers (
+            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+            title TEXT NOT NULL,
+            authors TEXT[] DEFAULT '{}',
+            abstract TEXT,
+            doi VARCHAR(255) UNIQUE,
+            file_path TEXT,
+            content_hash VARCHAR(64) UNIQUE,
+            is_preset BOOLEAN DEFAULT FALSE,
+            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+        );
+        
+        -- 用户论文关系表
+        CREATE TABLE IF NOT EXISTS user_paper_relations (
+            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+            user_id UUID REFERENCES users(id) ON DELETE CASCADE,
+            paper_id UUID REFERENCES papers(id) ON DELETE CASCADE,
+            tags TEXT[] DEFAULT '{}',
+            rating INTEGER DEFAULT 0,
+            is_read BOOLEAN DEFAULT FALSE,
+            added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+            UNIQUE(user_id, paper_id)
+        );
+        
+        -- 分析报告表
+        CREATE TABLE IF NOT EXISTS analysis_reports (
+            id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
+            paper_id UUID REFERENCES papers(id) ON DELETE CASCADE,
+            generated_for_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
+            summary TEXT,
+            innovation_point TEXT,
+            limitation TEXT,
+            future_idea TEXT,
+            vector_ids JSONB DEFAULT '{}',
+            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+        );
+        
+        -- 引用缓存表
+        CREATE TABLE IF NOT EXISTS reference_cache (
+            doi VARCHAR(255) PRIMARY KEY,
+            bibtex_std TEXT,
+            is_verified BOOLEAN DEFAULT FALSE,
+            last_check TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+        );
+        
+        -- 创建索引
+        CREATE INDEX IF NOT EXISTS idx_papers_content_hash ON papers(content_hash);
+        CREATE INDEX IF NOT EXISTS idx_papers_doi ON papers(doi);
+        CREATE INDEX IF NOT EXISTS idx_user_paper_relations_user_id ON user_paper_relations(user_id);
+        CREATE INDEX IF NOT EXISTS idx_user_paper_relations_paper_id ON user_paper_relations(paper_id);
+        CREATE INDEX IF NOT EXISTS idx_analysis_reports_paper_id ON analysis_reports(paper_id);
+        CREATE INDEX IF NOT EXISTS idx_analysis_reports_user_id ON analysis_reports(generated_for_user_id);
+        """
+        
+        async with self.pool.acquire() as conn:
+            await conn.execute(create_tables_sql)
+    
+    @asynccontextmanager
+    async def get_connection(self):
+        """获取数据库连接"""
+        if not self.pool:
+            await self.initialize()
+        
+        async with self.pool.acquire() as conn:
+            try:
+                yield conn
+            except Exception as e:
+                raise DatabaseException(f"数据库操作失败: {str(e)}")
+    
+    # 用户相关操作
+    async def create_user(self, email: str, profile: Dict = None) -> str:
+        """创建用户"""
+        async with self.get_connection() as conn:
+            user_id = await conn.fetchval(
+                "INSERT INTO users (email, profile) VALUES ($1, $2) RETURNING id",
+                email, json.dumps(profile or {})
+            )
+            return str(user_id)
+    
+    async def get_user(self, user_id: str) -> Optional[Dict]:
+        """获取用户信息"""
+        async with self.get_connection() as conn:
+            row = await conn.fetchrow(
+                "SELECT * FROM users WHERE id = $1", user_id
+            )
+            return dict(row) if row else None
+    
+    async def update_user_profile(self, user_id: str, profile: Dict) -> bool:
+        """更新用户配置"""
+        async with self.get_connection() as conn:
+            result = await conn.execute(
+                "UPDATE users SET profile = $1 WHERE id = $2",
+                json.dumps(profile), user_id
+            )
+            return result == "UPDATE 1"
+    
+    # 论文相关操作
+    async def create_paper(self, title: str, authors: List[str], 
+                          abstract: str = None, doi: str = None,
+                          file_path: str = None, content_hash: str = None,
+                          is_preset: bool = False) -> str:
+        """创建论文记录"""
+        async with self.get_connection() as conn:
+            paper_id = await conn.fetchval(
+                """
+                INSERT INTO papers (title, authors, abstract, doi, file_path, content_hash, is_preset)
+                VALUES ($1, $2, $3, $4, $5, $6, $7)
+                RETURNING id
+                """,
+                title, authors, abstract, doi, file_path, content_hash, is_preset
+            )
+            return str(paper_id)
+    
+    async def get_paper(self, paper_id: str) -> Optional[Dict]:
+        """获取论文信息"""
+        async with self.get_connection() as conn:
+            row = await conn.fetchrow(
+                "SELECT * FROM papers WHERE id = $1", paper_id
+            )
+            return dict(row) if row else None
+    
+    async def get_paper_by_hash(self, content_hash: str) -> Optional[Dict]:
+        """根据内容哈希获取论文"""
+        async with self.get_connection() as conn:
+            row = await conn.fetchrow(
+                "SELECT * FROM papers WHERE content_hash = $1", content_hash
+            )
+            return dict(row) if row else None
+    
+    async def search_papers(self, query: str, limit: int = 10, offset: int = 0) -> List[Dict]:
+        """搜索论文"""
+        async with self.get_connection() as conn:
+            rows = await conn.fetch(
+                """
+                SELECT * FROM papers 
+                WHERE title ILIKE $1 OR abstract ILIKE $1
+                ORDER BY created_at DESC
+                LIMIT $2 OFFSET $3
+                """,
+                f"%{query}%", limit, offset
+            )
+            return [dict(row) for row in rows]
+    
+    # 用户论文关系操作
+    async def add_paper_to_user(self, user_id: str, paper_id: str, 
+                               tags: List[str] = None, rating: int = 0) -> bool:
+        """将论文添加到用户库"""
+        async with self.get_connection() as conn:
+            try:
+                await conn.execute(
+                    """
+                    INSERT INTO user_paper_relations (user_id, paper_id, tags, rating)
+                    VALUES ($1, $2, $3, $4)
+                    ON CONFLICT (user_id, paper_id) DO UPDATE SET
+                        tags = EXCLUDED.tags,
+                        rating = EXCLUDED.rating,
+                        added_at = CURRENT_TIMESTAMP
+                    """,
+                    user_id, paper_id, tags or [], rating
+                )
+                return True
+            except Exception:
+                return False
+    
+    async def get_user_papers(self, user_id: str, limit: int = 50, offset: int = 0) -> List[Dict]:
+        """获取用户的论文列表"""
+        async with self.get_connection() as conn:
+            rows = await conn.fetch(
+                """
+                SELECT p.*, upr.tags, upr.rating, upr.is_read, upr.added_at
+                FROM papers p
+                JOIN user_paper_relations upr ON p.id = upr.paper_id
+                WHERE upr.user_id = $1
+                ORDER BY upr.added_at DESC
+                LIMIT $2 OFFSET $3
+                """,
+                user_id, limit, offset
+            )
+            return [dict(row) for row in rows]
+    
+    # 分析报告操作
+    async def create_analysis_report(self, paper_id: str, summary: str,
+                                   innovation_point: str, limitation: str,
+                                   future_idea: str, vector_ids: Dict = None,
+                                   user_id: str = None) -> str:
+        """创建分析报告"""
+        async with self.get_connection() as conn:
+            report_id = await conn.fetchval(
+                """
+                INSERT INTO analysis_reports 
+                (paper_id, generated_for_user_id, summary, innovation_point, limitation, future_idea, vector_ids)
+                VALUES ($1, $2, $3, $4, $5, $6, $7)
+                RETURNING id
+                """,
+                paper_id, user_id, summary, innovation_point, 
+                limitation, future_idea, json.dumps(vector_ids or {})
+            )
+            return str(report_id)
+    
+    async def get_analysis_report(self, paper_id: str, user_id: str = None) -> Optional[Dict]:
+        """获取分析报告"""
+        async with self.get_connection() as conn:
+            if user_id:
+                row = await conn.fetchrow(
+                    """
+                    SELECT * FROM analysis_reports 
+                    WHERE paper_id = $1 AND (generated_for_user_id = $2 OR generated_for_user_id IS NULL)
+                    ORDER BY created_at DESC LIMIT 1
+                    """,
+                    paper_id, user_id
+                )
+            else:
+                row = await conn.fetchrow(
+                    """
+                    SELECT * FROM analysis_reports 
+                    WHERE paper_id = $1 AND generated_for_user_id IS NULL
+                    ORDER BY created_at DESC LIMIT 1
+                    """,
+                    paper_id
+                )
+            return dict(row) if row else None
+    
+    # 引用缓存操作
+    async def cache_reference(self, doi: str, bibtex: str, is_verified: bool = False):
+        """缓存引用信息"""
+        async with self.get_connection() as conn:
+            await conn.execute(
+                """
+                INSERT INTO reference_cache (doi, bibtex_std, is_verified, last_check)
+                VALUES ($1, $2, $3, CURRENT_TIMESTAMP)
+                ON CONFLICT (doi) DO UPDATE SET
+                    bibtex_std = EXCLUDED.bibtex_std,
+                    is_verified = EXCLUDED.is_verified,
+                    last_check = CURRENT_TIMESTAMP
+                """,
+                doi, bibtex, is_verified
+            )
+    
+    async def get_cached_reference(self, doi: str) -> Optional[Dict]:
+        """获取缓存的引用信息"""
+        async with self.get_connection() as conn:
+            row = await conn.fetchrow(
+                "SELECT * FROM reference_cache WHERE doi = $1", doi
+            )
+            return dict(row) if row else None
+    
+    async def close(self):
+        """关闭数据库连接池"""
+        if self.pool:
+            await self.pool.close()
+
+# 全局数据库管理器实例
+db_manager = DatabaseManager()

+ 50 - 0
Co-creation-projects/Apricity-InnocoreAI/core/exceptions.py

@@ -0,0 +1,50 @@
+"""
+InnoCore AI 自定义异常类
+"""
+
+class InnoCoreException(Exception):
+    """InnoCore AI 基础异常类"""
+    def __init__(self, message: str, error_code: str = None):
+        self.message = message
+        self.error_code = error_code
+        super().__init__(self.message)
+
+class AgentException(InnoCoreException):
+    """Agent相关异常"""
+    pass
+
+class VectorStoreException(InnoCoreException):
+    """向量存储异常"""
+    pass
+
+class DatabaseException(InnoCoreException):
+    """数据库异常"""
+    pass
+
+class LLMException(InnoCoreException):
+    """LLM调用异常"""
+    pass
+
+class PDFParsingException(InnoCoreException):
+    """PDF解析异常"""
+    pass
+
+class ExternalAPIException(InnoCoreException):
+    """外部API调用异常"""
+    pass
+
+class ConfigurationException(InnoCoreException):
+    """配置异常"""
+    pass
+
+class ValidationException(InnoCoreException):
+    """数据验证异常"""
+    pass
+
+class TimeoutException(InnoCoreException):
+    """超时异常"""
+    pass
+
+class ResourceExhaustedException(InnoCoreException):
+    """资源耗尽异常"""
+    pass

+ 130 - 0
Co-creation-projects/Apricity-InnocoreAI/core/llm_adapter.py

@@ -0,0 +1,130 @@
+"""
+LLM 适配器 - 基于 HelloAgent 框架
+"""
+
+import logging
+from typing import Dict, Any, Optional
+from core.config import get_config
+
+logger = logging.getLogger(__name__)
+
+class LLMAdapter:
+    """LLM 适配器,基于 HelloAgent 框架"""
+    
+    def __init__(self):
+        """初始化 LLM 适配器"""
+        self.config = get_config()
+        self.llm = None
+        self._initialize_llm()
+    
+    def _initialize_llm(self):
+        """初始化 HelloAgent LLM"""
+        try:
+            from hello_agents import HelloAgentsLLM
+            
+            # 根据文档,HelloAgentsLLM 的初始化参数
+            self.llm = HelloAgentsLLM(
+                model=self.config.llm.model_name,
+                api_key=self.config.llm.api_key,
+                base_url=self.config.llm.base_url,
+                temperature=self.config.llm.temperature,
+                max_tokens=self.config.llm.max_tokens,
+                timeout=self.config.llm.timeout
+            )
+            logger.info(f"HelloAgent LLM 初始化成功: {self.config.llm.model_name}")
+        except ImportError as e:
+            logger.error(f"hello-agents 未安装: {str(e)}")
+            raise ImportError("请安装 hello-agents: pip install 'hello-agents[all]>=0.2.7'")
+        except Exception as e:
+            logger.error(f"HelloAgent LLM 初始化失败: {str(e)}")
+            raise
+    
+    def _format_messages(self, prompt: str) -> list:
+        """
+        将提示词格式化为消息列表
+        
+        Args:
+            prompt: 提示词字符串
+            
+        Returns:
+            消息列表,格式为 [{"role": "user", "content": "..."}]
+        """
+        if isinstance(prompt, str):
+            return [{"role": "user", "content": prompt}]
+        elif isinstance(prompt, list):
+            return prompt
+        else:
+            return [{"role": "user", "content": str(prompt)}]
+    
+    async def ainvoke(self, prompt: str, **kwargs) -> str:
+        """
+        异步调用 LLM
+        
+        Args:
+            prompt: 提示词(字符串或消息列表)
+            **kwargs: 额外参数
+            
+        Returns:
+            LLM 响应文本
+        """
+        try:
+            # 格式化消息
+            messages = self._format_messages(prompt)
+            
+            # HelloAgent 使用同步 invoke,在异步上下文中调用
+            import asyncio
+            response = await asyncio.to_thread(self.llm.invoke, messages, **kwargs)
+            
+            # 提取文本内容
+            if isinstance(response, str):
+                return response
+            elif hasattr(response, 'content'):
+                return response.content
+            elif hasattr(response, 'text'):
+                return response.text
+            else:
+                return str(response)
+        except Exception as e:
+            logger.error(f"LLM 异步调用失败: {str(e)}")
+            raise
+    
+    def invoke(self, prompt: str, **kwargs) -> str:
+        """
+        同步调用 LLM
+        
+        Args:
+            prompt: 提示词(字符串或消息列表)
+            **kwargs: 额外参数
+            
+        Returns:
+            LLM 响应文本
+        """
+        try:
+            # 格式化消息
+            messages = self._format_messages(prompt)
+            
+            # HelloAgent 的同步调用
+            response = self.llm.invoke(messages, **kwargs)
+            
+            # 提取文本内容
+            if isinstance(response, str):
+                return response
+            elif hasattr(response, 'content'):
+                return response.content
+            elif hasattr(response, 'text'):
+                return response.text
+            else:
+                return str(response)
+        except Exception as e:
+            logger.error(f"LLM 同步调用失败: {str(e)}")
+            raise
+
+# 全局 LLM 适配器实例
+_llm_adapter = None
+
+def get_llm_adapter() -> LLMAdapter:
+    """获取全局 LLM 适配器实例"""
+    global _llm_adapter
+    if _llm_adapter is None:
+        _llm_adapter = LLMAdapter()
+    return _llm_adapter

+ 281 - 0
Co-creation-projects/Apricity-InnocoreAI/core/vector_store.py

@@ -0,0 +1,281 @@
+"""
+InnoCore AI 向量存储管理模块
+"""
+
+import asyncio
+from typing import List, Dict, Optional, Any, Tuple
+import numpy as np
+from qdrant_client import QdrantClient
+from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
+from qdrant_client.http.models import CollectionInfo
+import hashlib
+import json
+
+from .config import get_config
+from .exceptions import VectorStoreException
+
+class VectorStoreManager:
+    """向量存储管理器"""
+    
+    def __init__(self):
+        self.config = get_config().vector_db
+        self.client = None
+        self.l1_collection = f"{self.config.collection_name_prefix}_l1_preset"
+        self.l2_collection = f"{self.config.collection_name_prefix}_l2_user"
+    
+    async def initialize(self):
+        """初始化向量数据库连接"""
+        try:
+            self.client = QdrantClient(
+                host=self.config.host,
+                port=self.config.port,
+                api_key=self.config.api_key
+            )
+            await self._create_collections()
+        except Exception as e:
+            raise VectorStoreException(f"向量数据库初始化失败: {str(e)}")
+    
+    async def _create_collections(self):
+        """创建向量集合"""
+        collections = [
+            (self.l1_collection, "L1预置库"),
+            (self.l2_collection, "L2用户库")
+        ]
+        
+        for collection_name, description in collections:
+            try:
+                self.client.get_collection(collection_name)
+            except Exception:
+                self.client.create_collection(
+                    collection_name=collection_name,
+                    vectors_config=VectorParams(
+                        size=1536,  # OpenAI embedding维度
+                        distance=Distance.COSINE
+                    )
+                )
+    
+    def _generate_point_id(self, content: str) -> str:
+        """生成向量点ID"""
+        return hashlib.md5(content.encode()).hexdigest()
+    
+    async def add_to_l1(self, paper_id: str, title: str, abstract: str, 
+                       content: str, metadata: Dict = None) -> str:
+        """添加到L1预置库"""
+        try:
+            # 生成embedding (这里需要调用实际的embedding服务)
+            embedding = await self._generate_embedding(f"{title} {abstract} {content}")
+            
+            point_id = self._generate_point_id(f"{paper_id}_l1")
+            
+            point = PointStruct(
+                id=point_id,
+                vector=embedding,
+                payload={
+                    "paper_id": paper_id,
+                    "title": title,
+                    "abstract": abstract,
+                    "content": content[:1000],  # 截取前1000字符
+                    "metadata": metadata or {},
+                    "collection_type": "l1",
+                    "created_at": str(asyncio.get_event_loop().time())
+                }
+            )
+            
+            self.client.upsert(
+                collection_name=self.l1_collection,
+                points=[point]
+            )
+            
+            return point_id
+            
+        except Exception as e:
+            raise VectorStoreException(f"添加到L1库失败: {str(e)}")
+    
+    async def add_to_l2(self, user_id: str, paper_id: str, title: str, 
+                       abstract: str, content: str, metadata: Dict = None) -> str:
+        """添加到L2用户库"""
+        try:
+            embedding = await self._generate_embedding(f"{title} {abstract} {content}")
+            
+            point_id = self._generate_point_id(f"{user_id}_{paper_id}_l2")
+            
+            point = PointStruct(
+                id=point_id,
+                vector=embedding,
+                payload={
+                    "user_id": user_id,
+                    "paper_id": paper_id,
+                    "title": title,
+                    "abstract": abstract,
+                    "content": content[:1000],
+                    "metadata": metadata or {},
+                    "collection_type": "l2",
+                    "created_at": str(asyncio.get_event_loop().time())
+                }
+            )
+            
+            self.client.upsert(
+                collection_name=self.l2_collection,
+                points=[point]
+            )
+            
+            return point_id
+            
+        except Exception as e:
+            raise VectorStoreException(f"添加到L2库失败: {str(e)}")
+    
+    async def hybrid_search(self, query: str, user_id: str = None, 
+                           top_k: int = 5, include_l1: bool = True,
+                           include_l2: bool = True) -> List[Dict]:
+        """混合搜索"""
+        try:
+            query_embedding = await self._generate_embedding(query)
+            results = []
+            
+            config = get_config()
+            vector_weight = config.hybrid_search_weights.get("vector", 0.7)
+            keyword_weight = config.hybrid_search_weights.get("keyword", 0.3)
+            
+            # L1库搜索
+            if include_l1:
+                l1_results = self.client.search(
+                    collection_name=self.l1_collection,
+                    query_vector=query_embedding,
+                    limit=top_k,
+                    with_payload=True
+                )
+                
+                for result in l1_results:
+                    results.append({
+                        "id": result.id,
+                        "score": result.score * vector_weight,
+                        "payload": result.payload,
+                        "collection_type": "l1"
+                    })
+            
+            # L2库搜索
+            if include_l2 and user_id:
+                # 构建用户过滤条件
+                user_filter = Filter(
+                    must=[
+                        FieldCondition(
+                            key="user_id",
+                            match=MatchValue(value=user_id)
+                        )
+                    ]
+                )
+                
+                l2_results = self.client.search(
+                    collection_name=self.l2_collection,
+                    query_vector=query_embedding,
+                    query_filter=user_filter,
+                    limit=top_k,
+                    with_payload=True
+                )
+                
+                for result in l2_results:
+                    results.append({
+                        "id": result.id,
+                        "score": result.score * vector_weight,
+                        "payload": result.payload,
+                        "collection_type": "l2"
+                    })
+            
+            # 关键词匹配加分
+            for result in results:
+                payload = result["payload"]
+                keyword_score = self._calculate_keyword_score(
+                    query, 
+                    f"{payload.get('title', '')} {payload.get('abstract', '')}"
+                )
+                result["score"] += keyword_score * keyword_weight
+            
+            # 按分数排序并返回top_k
+            results.sort(key=lambda x: x["score"], reverse=True)
+            return results[:top_k]
+            
+        except Exception as e:
+            raise VectorStoreException(f"混合搜索失败: {str(e)}")
+    
+    def _calculate_keyword_score(self, query: str, content: str) -> float:
+        """计算关键词匹配分数"""
+        query_words = set(query.lower().split())
+        content_words = set(content.lower().split())
+        
+        if not query_words:
+            return 0.0
+        
+        intersection = query_words.intersection(content_words)
+        return len(intersection) / len(query_words)
+    
+    async def _generate_embedding(self, text: str) -> List[float]:
+        """生成文本向量"""
+        # 这里应该调用实际的embedding服务
+        # 暂时返回随机向量作为示例
+        import random
+        return [random.random() for _ in range(1536)]
+    
+    async def get_user_vectors(self, user_id: str, limit: int = 100) -> List[Dict]:
+        """获取用户的向量数据"""
+        try:
+            user_filter = Filter(
+                must=[
+                    FieldCondition(
+                        key="user_id",
+                        match=MatchValue(value=user_id)
+                    )
+                ]
+            )
+            
+            results = self.client.scroll(
+                collection_name=self.l2_collection,
+                scroll_filter=user_filter,
+                limit=limit,
+                with_payload=True
+            )
+            
+            return [
+                {
+                    "id": point.id,
+                    "payload": point.payload
+                }
+                for point in results[0]
+            ]
+            
+        except Exception as e:
+            raise VectorStoreException(f"获取用户向量失败: {str(e)}")
+    
+    async def delete_user_vectors(self, user_id: str) -> bool:
+        """删除用户的所有向量数据"""
+        try:
+            user_filter = Filter(
+                must=[
+                    FieldCondition(
+                        key="user_id",
+                        match=MatchValue(value=user_id)
+                    )
+                ]
+            )
+            
+            self.client.delete(
+                collection_name=self.l2_collection,
+                points_selector=user_filter
+            )
+            
+            return True
+            
+        except Exception as e:
+            raise VectorStoreException(f"删除用户向量失败: {str(e)}")
+    
+    async def get_collection_info(self, collection_type: str = "l1") -> CollectionInfo:
+        """获取集合信息"""
+        collection_name = self.l1_collection if collection_type == "l1" else self.l2_collection
+        return self.client.get_collection(collection_name)
+    
+    async def close(self):
+        """关闭向量数据库连接"""
+        if self.client:
+            self.client.close()
+
+# 全局向量存储管理器实例
+vector_store_manager = VectorStoreManager()

+ 218 - 0
Co-creation-projects/Apricity-InnocoreAI/diagnose.py

@@ -0,0 +1,218 @@
+#!/usr/bin/env python3
+"""系统诊断脚本 - 检查所有配置和依赖"""
+
+import sys
+import os
+from pathlib import Path
+
+def check_env_file():
+    """检查 .env 文件"""
+    print("\n" + "="*60)
+    print("1. 检查环境配置文件")
+    print("="*60)
+    
+    env_path = Path(".env")
+    if not env_path.exists():
+        print("❌ .env 文件不存在")
+        return False
+    
+    print("✅ .env 文件存在")
+    
+    # 读取关键配置
+    with open(env_path, encoding='utf-8') as f:
+        content = f.read()
+        
+    required_keys = ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_MODEL"]
+    for key in required_keys:
+        if key in content:
+            print(f"✅ {key} 已配置")
+        else:
+            print(f"⚠️  {key} 未配置")
+    
+    return True
+
+def check_dependencies():
+    """检查依赖包"""
+    print("\n" + "="*60)
+    print("2. 检查依赖包")
+    print("="*60)
+    
+    required_packages = [
+        "fastapi",
+        "uvicorn",
+        "hello_agents",
+        "arxiv",
+        "httpx",
+        "asyncpg",
+        "qdrant_client",
+        "feedparser",
+        "beautifulsoup4"
+    ]
+    
+    missing = []
+    for package in required_packages:
+        try:
+            # 特殊处理包名映射
+            import_name = package.replace("-", "_")
+            if package == "beautifulsoup4":
+                import_name = "bs4"
+            __import__(import_name)
+            print(f"✅ {package}")
+        except ImportError:
+            print(f"❌ {package} - 缺失")
+            missing.append(package)
+    
+    if missing:
+        print(f"\n⚠️  缺失的包: {', '.join(missing)}")
+        print(f"安装命令: pip install {' '.join(missing)}")
+        return False
+    
+    return True
+
+def check_config():
+    """检查配置加载"""
+    print("\n" + "="*60)
+    print("3. 检查配置加载")
+    print("="*60)
+    
+    try:
+        from core.config import get_config
+        config = get_config()
+        
+        print(f"✅ 配置加载成功")
+        print(f"   - API Key: {'已设置' if config.llm.api_key else '未设置'}")
+        print(f"   - Base URL: {config.llm.base_url or '未设置'}")
+        print(f"   - Model: {config.llm.model_name}")
+        print(f"   - Debug: {config.debug}")
+        
+        return True
+    except Exception as e:
+        print(f"❌ 配置加载失败: {str(e)}")
+        return False
+
+def check_api_routes():
+    """检查 API 路由"""
+    print("\n" + "="*60)
+    print("4. 检查 API 路由")
+    print("="*60)
+    
+    try:
+        from api.main import app
+        
+        routes = []
+        for route in app.routes:
+            if hasattr(route, 'path'):
+                routes.append(route.path)
+        
+        print(f"✅ API 加载成功,共 {len(routes)} 个路由")
+        
+        # 检查关键路由
+        key_routes = ["/", "/health", "/api/v1/papers/search", "/api/v1/analysis/analyze"]
+        for route in key_routes:
+            if route in routes:
+                print(f"   ✅ {route}")
+            else:
+                print(f"   ❌ {route} - 缺失")
+        
+        return True
+    except Exception as e:
+        print(f"❌ API 加载失败: {str(e)}")
+        import traceback
+        traceback.print_exc()
+        return False
+
+def check_frontend():
+    """检查前端文件"""
+    print("\n" + "="*60)
+    print("5. 检查前端文件")
+    print("="*60)
+    
+    frontend_files = [
+        "frontend/index.html",
+        "frontend/static/css/style.css",
+        "frontend/static/js/app.js"
+    ]
+    
+    all_exist = True
+    for file_path in frontend_files:
+        path = Path(file_path)
+        if path.exists():
+            print(f"✅ {file_path}")
+        else:
+            print(f"⚠️  {file_path} - 不存在(可选)")
+    
+    return True
+
+def check_llm_connection():
+    """检查 LLM 连接"""
+    print("\n" + "="*60)
+    print("6. 检查 LLM 连接")
+    print("="*60)
+    
+    try:
+        import asyncio
+        from hello_agents import HelloAgentsLLM
+        from core.config import get_config
+        
+        config = get_config()
+        
+        if not config.llm.api_key:
+            print("⚠️  API Key 未设置,跳过连接测试")
+            return True
+        
+        async def test():
+            from core.llm_adapter import get_llm_adapter
+            adapter = get_llm_adapter()
+            
+            response = await adapter.ainvoke("你好")
+            return response
+        
+        print("正在测试 LLM 连接...")
+        result = asyncio.run(test())
+        print(f"✅ LLM 连接成功")
+        print(f"   模型响应: {result[:50]}...")
+        
+        return True
+    except Exception as e:
+        error_msg = str(e)
+        # 如果是 API 格式错误,说明连接是通的,只是请求格式问题
+        if "400" in error_msg or "invalid_request" in error_msg:
+            print(f"⚠️  LLM API 可访问,但请求格式需要调整")
+            print(f"   错误信息: {error_msg[:100]}...")
+            return True  # 认为通过,因为连接本身是正常的
+        print(f"❌ LLM 连接失败: {error_msg[:100]}...")
+        return False
+
+def main():
+    """主函数"""
+    print("\n" + "="*60)
+    print("InnoCore AI 系统诊断")
+    print("="*60)
+    
+    results = []
+    
+    results.append(("环境配置", check_env_file()))
+    results.append(("依赖包", check_dependencies()))
+    results.append(("配置加载", check_config()))
+    results.append(("API 路由", check_api_routes()))
+    results.append(("前端文件", check_frontend()))
+    results.append(("LLM 连接", check_llm_connection()))
+    
+    # 总结
+    print("\n" + "="*60)
+    print("诊断结果总结")
+    print("="*60)
+    
+    for name, result in results:
+        status = "✅ 通过" if result else "❌ 失败"
+        print(f"{name}: {status}")
+    
+    all_passed = all(r[1] for r in results)
+    if all_passed:
+        print("\n🎉 所有检查通过!系统可以正常运行。")
+        print("\n启动命令: python run.py")
+    else:
+        print("\n⚠️  部分检查未通过,请根据上述提示修复问题。")
+
+if __name__ == "__main__":
+    main()

+ 175 - 0
Co-creation-projects/Apricity-InnocoreAI/docs/MODEL_GUIDE.md

@@ -0,0 +1,175 @@
+# InnoCore AI 模型选择指南
+
+## 推荐模型配置
+
+### 1. OpenAI(国际用户推荐)
+
+**优点:** 稳定、API 简单、效果好
+**缺点:** 需要国际网络、按 token 计费
+
+```bash
+# .env 配置
+OPENAI_API_KEY=sk-your-key-here
+OPENAI_BASE_URL=https://api.openai.com/v1
+LLM_PROVIDER=openai
+LLM_MODEL=gpt-3.5-turbo  # 或 gpt-4
+```
+
+**模型选择:**
+- `gpt-3.5-turbo` - 快速、便宜,适合日常使用
+- `gpt-4` - 更强大,适合复杂分析
+- `gpt-4-turbo-preview` - 最新版本,上下文更长
+
+---
+
+### 2. 阿里云灵积 DashScope(国内用户推荐)⭐
+
+**优点:** 国内访问快、中文理解好、价格实惠
+**缺点:** 需要阿里云账号
+
+```bash
+# .env 配置
+DASHSCOPE_API_KEY=sk-your-dashscope-key
+LLM_PROVIDER=dashscope
+LLM_MODEL=qwen-turbo
+```
+
+**模型选择:**
+- `qwen-turbo` - 快速响应,适合实时交互(推荐)
+- `qwen-plus` - 平衡性能和成本
+- `qwen-max` - 最强性能,适合复杂任务
+
+**获取 API Key:**
+1. 访问 https://dashscope.console.aliyun.com/
+2. 注册/登录阿里云账号
+3. 开通灵积服务
+4. 创建 API Key
+
+---
+
+### 3. ModelScope(本地部署)
+
+**优点:** 完全免费、数据隐私、可定制
+**缺点:** 需要 GPU、部署复杂
+
+#### 推荐模型:
+
+**文本分析(当前需求):**
+- `Qwen2.5-7B-Instruct` - 7B 参数,需要 16GB 显存
+- `Qwen2.5-14B-Instruct` - 14B 参数,需要 32GB 显存
+- `GLM-4-9B` - 9B 参数,中文理解好
+
+**多模态(图表理解):**
+- `Qwen2-VL-7B-Instruct` - 能理解论文图表
+- `InternVL2-8B` - 学术场景表现好
+
+**本地部署步骤:**
+
+```bash
+# 1. 安装依赖
+pip install modelscope transformers torch
+
+# 2. 下载模型
+from modelscope import snapshot_download
+model_dir = snapshot_download('qwen/Qwen2.5-7B-Instruct')
+
+# 3. 启动推理服务(使用 vLLM 或 FastChat)
+python -m vllm.entrypoints.openai.api_server \
+    --model qwen/Qwen2.5-7B-Instruct \
+    --host 0.0.0.0 \
+    --port 8001
+
+# 4. 配置 .env
+OPENAI_BASE_URL=http://localhost:8001/v1
+OPENAI_API_KEY=dummy  # 本地部署不需要真实 key
+LLM_MODEL=qwen/Qwen2.5-7B-Instruct
+```
+
+---
+
+## 针对不同场景的推荐
+
+### 场景 1:快速开发测试
+**推荐:** OpenAI gpt-3.5-turbo
+- 最简单,开箱即用
+- 适合原型开发
+
+### 场景 2:生产环境(国内)
+**推荐:** DashScope qwen-turbo ⭐⭐⭐
+- 访问速度快
+- 中文理解好
+- 成本可控
+
+### 场景 3:数据隐私要求高
+**推荐:** 本地部署 Qwen2.5-7B
+- 数据不出本地
+- 完全可控
+
+### 场景 4:需要理解论文图表
+**推荐:** Qwen2-VL-7B-Instruct
+- 多模态能力
+- 能理解公式和图表
+
+---
+
+## 性能对比
+
+| 模型 | 中文能力 | 英文能力 | 速度 | 成本 | 推荐度 |
+|------|---------|---------|------|------|--------|
+| GPT-3.5-turbo | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 快 | 中 | ⭐⭐⭐⭐ |
+| GPT-4 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 慢 | 高 | ⭐⭐⭐⭐ |
+| Qwen-turbo | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 快 | 低 | ⭐⭐⭐⭐⭐ |
+| Qwen-max | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 中 | 中 | ⭐⭐⭐⭐⭐ |
+| Qwen2.5-7B (本地) | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 中 | 免费 | ⭐⭐⭐⭐ |
+
+---
+
+## 快速开始
+
+### 方案 A:使用 OpenAI(最简单)
+
+1. 获取 API Key: https://platform.openai.com/api-keys
+2. 编辑 `.env` 文件:
+```bash
+OPENAI_API_KEY=sk-your-key-here
+```
+3. 重启服务器
+
+### 方案 B:使用阿里云灵积(推荐国内用户)⭐
+
+1. 获取 API Key: https://dashscope.console.aliyun.com/
+2. 编辑 `.env` 文件:
+```bash
+DASHSCOPE_API_KEY=sk-your-key-here
+LLM_PROVIDER=dashscope
+LLM_MODEL=qwen-turbo
+```
+3. 安装依赖:
+```bash
+pip install dashscope
+```
+4. 重启服务器
+
+---
+
+## 常见问题
+
+**Q: 哪个模型最适合科研论文分析?**
+A: 推荐 Qwen-max(DashScope)或 GPT-4,它们对学术文本理解最好。
+
+**Q: 如何降低成本?**
+A: 使用 qwen-turbo 或本地部署 Qwen2.5-7B。
+
+**Q: 需要处理论文中的图表怎么办?**
+A: 使用多模态模型如 Qwen2-VL-7B-Instruct。
+
+**Q: 本地部署需要什么配置?**
+A: 最低 16GB 显存的 GPU(如 RTX 4090、A100)。
+
+---
+
+## 技术支持
+
+- ModelScope: https://www.modelscope.cn/
+- DashScope: https://help.aliyun.com/zh/dashscope/
+- OpenAI: https://platform.openai.com/docs

BIN
Co-creation-projects/Apricity-InnocoreAI/docs/screenshots/01-主界面.png


BIN
Co-creation-projects/Apricity-InnocoreAI/docs/screenshots/02-论文搜索.png


BIN
Co-creation-projects/Apricity-InnocoreAI/docs/screenshots/03-论文分析.png


+ 1624 - 0
Co-creation-projects/Apricity-InnocoreAI/frontend/index.html

@@ -0,0 +1,1624 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>研创·智核 - InnoCore AI</title>
+    <style>
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+        }
+
+        body {
+            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            min-height: 100vh;
+            color: #333;
+        }
+
+        .container {
+            max-width: 1200px;
+            margin: 0 auto;
+            padding: 20px;
+        }
+
+        header {
+            text-align: center;
+            padding: 40px 0;
+            color: white;
+        }
+
+        header h1 {
+            font-size: 3rem;
+            margin-bottom: 10px;
+            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
+        }
+
+        header p {
+            font-size: 1.2rem;
+            opacity: 0.9;
+        }
+
+        .main-content {
+            background: white;
+            border-radius: 15px;
+            padding: 40px;
+            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
+            margin-bottom: 30px;
+        }
+
+        .features {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+            gap: 30px;
+            margin-top: 30px;
+        }
+
+        .feature-card {
+            background: #f8f9fa;
+            padding: 25px;
+            border-radius: 10px;
+            border-left: 4px solid #667eea;
+            transition: transform 0.3s ease, box-shadow 0.3s ease;
+            cursor: pointer;
+        }
+
+        .feature-card:hover {
+            transform: translateY(-5px);
+            box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
+        }
+
+        .feature-card.active {
+            background: #e8f0fe;
+            border-left-color: #4285f4;
+        }
+
+        .feature-card h3 {
+            color: #667eea;
+            margin-bottom: 10px;
+            font-size: 1.3rem;
+        }
+
+        .feature-card p {
+            color: #666;
+            line-height: 1.6;
+            margin-bottom: 15px;
+        }
+
+        .btn {
+            background: #667eea;
+            color: white;
+            padding: 8px 16px;
+            border: none;
+            border-radius: 6px;
+            cursor: pointer;
+            font-size: 0.9rem;
+            transition: background 0.3s ease;
+        }
+
+        .btn:hover {
+            background: #5a6fd8;
+        }
+
+        .btn:disabled {
+            background: #ccc;
+            cursor: not-allowed;
+        }
+
+        .status {
+            background: #e8f5e8;
+            border: 1px solid #4caf50;
+            color: #2e7d32;
+            padding: 15px;
+            border-radius: 8px;
+            margin: 20px 0;
+        }
+
+        .interaction-panel {
+            background: #f8f9fa;
+            border-radius: 10px;
+            padding: 30px;
+            margin-top: 30px;
+            display: none;
+        }
+
+        .interaction-panel.active {
+            display: block;
+        }
+
+        .input-group {
+            margin-bottom: 20px;
+        }
+
+        .input-group label {
+            display: block;
+            margin-bottom: 8px;
+            font-weight: 600;
+            color: #333;
+        }
+
+        .input-group input,
+        .input-group textarea,
+        .input-group select {
+            width: 100%;
+            padding: 12px;
+            border: 2px solid #e1e5e9;
+            border-radius: 8px;
+            font-size: 1rem;
+            transition: border-color 0.3s ease;
+        }
+
+        .input-group input:focus,
+        .input-group textarea:focus,
+        .input-group select:focus {
+            outline: none;
+            border-color: #667eea;
+        }
+
+        .input-group textarea {
+            min-height: 120px;
+            resize: vertical;
+        }
+
+        .response-area {
+            background: #f8f9fa;
+            border: 2px solid #e1e5e9;
+            border-radius: 8px;
+            padding: 20px;
+            margin-top: 20px;
+            min-height: 200px;
+            max-height: 500px;
+            overflow-y: auto;
+            font-family: 'Consolas', 'Monaco', monospace;
+        }
+
+        .response-area pre {
+            background: white;
+            padding: 15px;
+            border-radius: 6px;
+            border-left: 4px solid #667eea;
+            overflow-x: auto;
+            margin: 10px 0;
+            font-size: 0.9rem;
+            line-height: 1.6;
+        }
+
+        .paper-card {
+            background: white;
+            border: 1px solid #e1e5e9;
+            border-radius: 8px;
+            padding: 20px;
+            margin: 15px 0;
+            transition: all 0.3s ease;
+            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+        }
+
+        .paper-card:hover {
+            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+            transform: translateY(-2px);
+        }
+
+        .paper-card h4 {
+            color: #667eea;
+            margin-bottom: 10px;
+            font-size: 1.1rem;
+            line-height: 1.4;
+        }
+
+        .paper-card .authors {
+            color: #666;
+            font-size: 0.9rem;
+            margin: 8px 0;
+        }
+
+        .paper-card .abstract {
+            color: #444;
+            line-height: 1.6;
+            margin: 12px 0;
+            font-size: 0.95rem;
+        }
+
+        .paper-card .meta {
+            display: flex;
+            gap: 15px;
+            margin-top: 12px;
+            font-size: 0.85rem;
+            color: #888;
+        }
+
+        .paper-card .meta span {
+            display: inline-flex;
+            align-items: center;
+            gap: 5px;
+        }
+
+        .paper-card a {
+            color: #667eea;
+            text-decoration: none;
+            font-weight: 500;
+        }
+
+        .paper-card a:hover {
+            text-decoration: underline;
+        }
+
+        .result-header {
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            color: white;
+            padding: 15px 20px;
+            border-radius: 8px 8px 0 0;
+            margin: -20px -20px 20px -20px;
+            font-weight: 600;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+        }
+
+        .result-stats {
+            background: rgba(255, 255, 255, 0.2);
+            padding: 5px 12px;
+            border-radius: 20px;
+            font-size: 0.9rem;
+        }
+
+        .loading {
+            display: none;
+            text-align: center;
+            padding: 20px;
+            color: #667eea;
+        }
+
+        .loading.active {
+            display: block;
+        }
+
+        .spinner {
+            border: 3px solid #f3f3f3;
+            border-top: 3px solid #667eea;
+            border-radius: 50%;
+            width: 40px;
+            height: 40px;
+            animation: spin 1s linear infinite;
+            margin: 0 auto 10px;
+        }
+
+        @keyframes spin {
+            0% {
+                transform: rotate(0deg);
+            }
+
+            100% {
+                transform: rotate(360deg);
+            }
+        }
+
+        .error {
+            background: #ffebee;
+            border: 1px solid #f44336;
+            color: #c62828;
+            padding: 15px;
+            border-radius: 8px;
+            margin: 10px 0;
+        }
+
+        .success {
+            background: #e8f5e8;
+            border: 1px solid #4caf50;
+            color: #2e7d32;
+            padding: 15px;
+            border-radius: 8px;
+            margin: 10px 0;
+        }
+
+        .nav-buttons {
+            display: flex;
+            gap: 15px;
+            justify-content: center;
+            margin: 30px 0;
+            flex-wrap: wrap;
+        }
+
+        .nav-buttons .btn {
+            padding: 12px 25px;
+            font-size: 1rem;
+            text-decoration: none;
+            display: inline-block;
+        }
+
+        .file-upload {
+            border: 2px dashed #667eea;
+            border-radius: 8px;
+            padding: 30px;
+            text-align: center;
+            cursor: pointer;
+            transition: background-color 0.3s ease;
+        }
+
+        .file-upload:hover {
+            background: #f8f9ff;
+        }
+
+        .file-upload.dragover {
+            background: #e8f0fe;
+            border-color: #4285f4;
+        }
+
+        .copy-btn {
+            float: right;
+            background: rgba(102, 126, 234, 0.1);
+            border: 1px solid #667eea;
+            color: #667eea;
+            padding: 5px 12px;
+            border-radius: 4px;
+            cursor: pointer;
+            font-size: 0.85rem;
+            transition: all 0.3s ease;
+            margin-left: 10px;
+        }
+
+        .copy-btn:hover {
+            background: #667eea;
+            color: white;
+        }
+
+        .empty-state {
+            text-align: center;
+            padding: 40px 20px;
+            color: #999;
+            font-size: 1.1rem;
+        }
+
+        .badge {
+            display: inline-block;
+            padding: 4px 10px;
+            border-radius: 12px;
+            font-size: 0.8rem;
+            font-weight: 600;
+            margin-left: 8px;
+        }
+
+        .badge-success {
+            background: #e8f5e8;
+            color: #2e7d32;
+        }
+
+        .badge-info {
+            background: #e3f2fd;
+            color: #1976d2;
+        }
+
+        .badge-warning {
+            background: #fff3e0;
+            color: #f57c00;
+        }
+
+        .json-key {
+            color: #0066cc;
+            font-weight: 600;
+        }
+
+        .json-string {
+            color: #008000;
+        }
+
+        .json-number {
+            color: #ff6600;
+        }
+
+        .json-boolean {
+            color: #cc00cc;
+            font-weight: 600;
+        }
+
+        .json-null {
+            color: #999;
+            font-style: italic;
+        }
+
+        .markdown-content {
+            line-height: 1.8;
+            color: #333;
+        }
+
+        .markdown-content h1,
+        .markdown-content h2,
+        .markdown-content h3 {
+            color: #667eea;
+            margin-top: 20px;
+            margin-bottom: 10px;
+        }
+
+        .markdown-content ul {
+            list-style-type: disc;
+            padding-left: 25px;
+            margin: 15px 0;
+        }
+
+        .markdown-content li {
+            margin: 10px 0;
+            line-height: 1.8;
+        }
+
+        .markdown-content strong {
+            color: #333;
+            font-weight: 600;
+        }
+
+        .markdown-content code {
+            background: #f0f0f0;
+            padding: 2px 6px;
+            border-radius: 3px;
+            font-family: 'Consolas', 'Monaco', monospace;
+            color: #e83e8c;
+            font-size: 0.9em;
+        }
+
+        .markdown-content pre {
+            background: #f5f5f5;
+            padding: 15px;
+            border-radius: 6px;
+            overflow-x: auto;
+            border-left: 4px solid #667eea;
+            margin: 15px 0;
+        }
+
+        .markdown-content pre code {
+            background: none;
+            padding: 0;
+            color: #333;
+        }
+
+        .markdown-content a {
+            color: #667eea;
+            text-decoration: none;
+            border-bottom: 1px solid #667eea;
+        }
+
+        .markdown-content a:hover {
+            color: #5a6fd8;
+            border-bottom-color: #5a6fd8;
+        }
+
+        .markdown-content p {
+            margin: 12px 0;
+            line-height: 1.8;
+        }
+    </style>
+</head>
+
+<body>
+    <div class="container">
+        <header>
+            <h1>🧠 研创·智核</h1>
+            <p>基于多智能体架构的智能科研助手平台</p>
+        </header>
+
+        <main class="main-content">
+            <div class="status" id="systemStatus">
+                ✅ 系统初始化中...
+            </div>
+
+            <div class="nav-buttons">
+                <a href="/docs" class="btn" target="_blank">📚 API 文档</a>
+                <a href="/health" class="btn" target="_blank">🔍 健康检查</a>
+                <button class="btn" onclick="checkSystemStatus()">🔄 刷新状态</button>
+            </div>
+
+            <section>
+                <h2>🚀 核心功能</h2>
+
+                <!-- 工作流模式选择 -->
+                <div
+                    style="margin-bottom: 20px; padding: 15px; background: #f0f7ff; border-radius: 8px; border-left: 4px solid #667eea;">
+                    <h3 style="margin: 0 0 10px 0; color: #667eea;">🔄 工作模式</h3>
+                    <div style="display: flex; gap: 15px; flex-wrap: wrap;">
+                        <label style="display: flex; align-items: center; cursor: pointer;">
+                            <input type="radio" name="work-mode" value="individual" checked style="margin-right: 8px;">
+                            <span>单独模式 - 独立使用每个智能体</span>
+                        </label>
+                        <label style="display: flex; align-items: center; cursor: pointer;">
+                            <input type="radio" name="work-mode" value="workflow" style="margin-right: 8px;">
+                            <span>协调模式 - 自动化完整工作流 ⭐</span>
+                        </label>
+                    </div>
+                </div>
+
+                <div class="features">
+                    <div class="feature-card" onclick="activateAgent('workflow')" id="workflow-card"
+                        style="display: none; grid-column: 1 / -1; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
+                        <h3>🔄 完整工作流</h3>
+                        <p>自动协调所有智能体:搜索论文 → 深度分析 → 生成引用 → 撰写报告</p>
+                        <button class="btn" style="background: white; color: #667eea;"
+                            onclick="event.stopPropagation(); activateAgent('workflow')">启动工作流</button>
+                    </div>
+
+                    <div class="feature-card" onclick="activateAgent('hunter')" id="hunter-card">
+                        <h3>🕵️ Hunter Agent</h3>
+                        <p>智能文献搜索与监控,自动抓取 ArXiv、IEEE 等平台的最新论文</p>
+                        <button class="btn" onclick="event.stopPropagation(); activateAgent('hunter')">开始搜索</button>
+                    </div>
+
+                    <div class="feature-card" onclick="activateAgent('miner')" id="miner-card">
+                        <h3>🧠 Miner Agent</h3>
+                        <p>深度论文分析,挖掘创新点,生成结构化研究报告</p>
+                        <button class="btn" onclick="event.stopPropagation(); activateAgent('miner')">分析论文</button>
+                    </div>
+
+                    <div class="feature-card" onclick="activateAgent('coach')" id="coach-card">
+                        <h3>✍️ Coach Agent</h3>
+                        <p>个性化写作助手,提供学术润色、风格迁移和实时指导</p>
+                        <button class="btn" onclick="event.stopPropagation(); activateAgent('coach')">写作助手</button>
+                    </div>
+
+                    <div class="feature-card" onclick="activateAgent('validator')" id="validator-card">
+                        <h3>🔎 Validator Agent</h3>
+                        <p>引用格式校验,确保学术引用的准确性和标准化</p>
+                        <button class="btn" onclick="event.stopPropagation(); activateAgent('validator')">校验引用</button>
+                    </div>
+                </div>
+            </section>
+
+            <!-- Hunter Agent 交互面板 -->
+            <div class="interaction-panel" id="hunter-panel">
+                <h3>🕵️ 文献搜索</h3>
+                <div class="input-group">
+                    <label for="search-keywords">搜索关键词</label>
+                    <input type="text" id="search-keywords"
+                        placeholder="例如: machine learning, natural language processing">
+                </div>
+                <div class="input-group">
+                    <label for="search-source">搜索来源</label>
+                    <select id="search-source">
+                        <option value="arxiv">ArXiv</option>
+                        <option value="ieee">IEEE</option>
+                        <option value="pubmed">PubMed</option>
+                        <option value="all">全部来源</option>
+                    </select>
+                </div>
+                <div class="input-group">
+                    <label for="search-limit">论文数量限制</label>
+                    <input type="number" id="search-limit" value="10" min="1" max="50">
+                </div>
+                <button class="btn" onclick="executeHunterTask()">开始搜索</button>
+                <div class="loading" id="hunter-loading">
+                    <div class="spinner"></div>
+                    <p>正在搜索论文...</p>
+                </div>
+                <div class="response-area" id="hunter-response"></div>
+            </div>
+
+            <!-- Miner Agent 交互面板 -->
+            <div class="interaction-panel" id="miner-panel">
+                <h3>🧠 论文分析</h3>
+                <div class="input-group">
+                    <label for="paper-upload">上传论文 (PDF)</label>
+                    <div class="file-upload" id="paper-upload-area">
+                        <p>📄 点击或拖拽PDF文件到此处</p>
+                        <input type="file" id="paper-file" accept=".pdf" style="display: none;">
+                    </div>
+                </div>
+                <div class="input-group">
+                    <label for="paper-url">或输入论文URL</label>
+                    <input type="url" id="paper-url" placeholder="https://arxiv.org/abs/...">
+                </div>
+                <div class="input-group">
+                    <label for="analysis-type">分析类型</label>
+                    <select id="analysis-type">
+                        <option value="summary">内容摘要</option>
+                        <option value="innovation">创新点分析</option>
+                        <option value="comparison">对比分析</option>
+                        <option value="comprehensive">综合分析</option>
+                    </select>
+                </div>
+                <button class="btn" onclick="executeMinerTask()">开始分析</button>
+                <div class="loading" id="miner-loading">
+                    <div class="spinner"></div>
+                    <p>正在分析论文...</p>
+                </div>
+                <div class="response-area" id="miner-response"></div>
+            </div>
+
+            <!-- Coach Agent 交互面板 -->
+            <div class="interaction-panel" id="coach-panel">
+                <h3>✍️ 写作助手</h3>
+                <div class="input-group">
+                    <label for="writing-text">您的文本</label>
+                    <textarea id="writing-text" placeholder="请输入需要润色的学术文本..."></textarea>
+                </div>
+                <div class="input-group">
+                    <label for="writing-style">写作风格</label>
+                    <select id="writing-style">
+                        <option value="formal">正式学术风格</option>
+                        <option value="nature">Nature风格</option>
+                        <option value="science">Science风格</option>
+                        <option value="ieee">IEEE风格</option>
+                    </select>
+                </div>
+                <div class="input-group">
+                    <label for="writing-task">任务类型</label>
+                    <select id="writing-task">
+                        <option value="polish">润色改进</option>
+                        <option value="translate">中译英</option>
+                        <option value="explain">解释概念</option>
+                        <option value="expand">内容扩展</option>
+                    </select>
+                </div>
+                <button class="btn" onclick="executeCoachTask()">开始处理</button>
+                <div class="loading" id="coach-loading">
+                    <div class="spinner"></div>
+                    <p>正在处理文本...</p>
+                </div>
+                <div class="response-area" id="coach-response"></div>
+            </div>
+
+            <!-- Validator Agent 交互面板 -->
+            <div class="interaction-panel" id="validator-panel">
+                <h3>🔎 引用校验</h3>
+                <div class="input-group">
+                    <label for="citation-input">引用信息</label>
+                    <textarea id="citation-input" placeholder="请输入引用信息,可以是DOI、标题、或BibTeX格式..."></textarea>
+                </div>
+                <div class="input-group">
+                    <label for="citation-format">输出格式</label>
+                    <select id="citation-format">
+                        <option value="bibtex">BibTeX</option>
+                        <option value="apa">APA</option>
+                        <option value="ieee">IEEE</option>
+                        <option value="mla">MLA</option>
+                    </select>
+                </div>
+                <button class="btn" onclick="executeValidatorTask()">校验引用</button>
+                <div class="loading" id="validator-loading">
+                    <div class="spinner"></div>
+                    <p>正在校验引用...</p>
+                </div>
+                <div class="response-area" id="validator-response"></div>
+            </div>
+
+            <!-- Workflow 协调面板 -->
+            <div class="interaction-panel" id="workflow-panel">
+                <h3>🔄 完整工作流</h3>
+                <p style="color: #666; margin-bottom: 20px;">自动协调所有智能体完成完整的研究流程</p>
+
+                <div class="input-group">
+                    <label for="workflow-keywords">研究关键词</label>
+                    <input type="text" id="workflow-keywords" placeholder="例如: deep learning for computer vision">
+                </div>
+
+                <div class="input-group">
+                    <label for="workflow-limit">搜索论文数量</label>
+                    <select id="workflow-limit">
+                        <option value="3">3篇(快速)</option>
+                        <option value="5" selected>5篇(推荐)</option>
+                        <option value="10">10篇(详细)</option>
+                    </select>
+                </div>
+
+                <div class="input-group">
+                    <label for="workflow-analysis-type">分析类型</label>
+                    <select id="workflow-analysis-type">
+                        <option value="summary">摘要分析</option>
+                        <option value="innovation">创新点分析</option>
+                        <option value="comparison">对比分析</option>
+                        <option value="comprehensive">综合分析</option>
+                    </select>
+                </div>
+
+                <div class="input-group">
+                    <label for="workflow-citation-format">引用格式</label>
+                    <select id="workflow-citation-format">
+                        <option value="bibtex">BibTeX</option>
+                        <option value="apa">APA</option>
+                        <option value="ieee">IEEE</option>
+                        <option value="mla">MLA</option>
+                    </select>
+                </div>
+
+                <div class="input-group">
+                    <label>
+                        <input type="checkbox" id="workflow-generate-report" checked>
+                        生成综合报告
+                    </label>
+                </div>
+
+                <button class="btn" onclick="executeWorkflow()"
+                    style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">🚀 启动完整工作流</button>
+
+                <div class="loading" id="workflow-loading">
+                    <div class="spinner"></div>
+                    <p>工作流执行中,请稍候...</p>
+                </div>
+
+                <div class="response-area" id="workflow-response"></div>
+            </div>
+        </main>
+    </div>
+
+    <script>
+        let currentAgent = null;
+
+        // 页面加载时检查系统状态
+        document.addEventListener('DOMContentLoaded', function () {
+            checkSystemStatus();
+            setupFileUpload();
+            setupWorkModeSwitch();
+        });
+
+        // 设置工作模式切换
+        function setupWorkModeSwitch() {
+            const modeRadios = document.querySelectorAll('input[name="work-mode"]');
+            modeRadios.forEach(radio => {
+                radio.addEventListener('change', function () {
+                    const workflowCard = document.getElementById('workflow-card');
+                    if (this.value === 'workflow') {
+                        workflowCard.style.display = 'block';
+                    } else {
+                        workflowCard.style.display = 'none';
+                    }
+                });
+            });
+        }
+
+        // 检查系统状态
+        async function checkSystemStatus() {
+            const statusDiv = document.getElementById('systemStatus');
+            try {
+                const response = await fetch('/health');
+                const data = await response.json();
+
+                if (response.ok) {
+                    statusDiv.className = 'status';
+                    statusDiv.innerHTML = `✅ 系统运行正常 | 状态: ${data.status} | 时间: ${new Date().toLocaleString()}`;
+                } else {
+                    throw new Error('系统状态异常');
+                }
+            } catch (error) {
+                statusDiv.className = 'error';
+                statusDiv.innerHTML = `❌ 系统连接失败: ${error.message}`;
+            }
+        }
+
+        // 激活智能体
+        function activateAgent(agentType) {
+            // 重置所有卡片和面板
+            document.querySelectorAll('.feature-card').forEach(card => {
+                card.classList.remove('active');
+            });
+            document.querySelectorAll('.interaction-panel').forEach(panel => {
+                panel.classList.remove('active');
+            });
+
+            // 激活选中的智能体
+            currentAgent = agentType;
+            document.getElementById(`${agentType}-card`).classList.add('active');
+            document.getElementById(`${agentType}-panel`).classList.add('active');
+        }
+
+        // 执行Hunter任务
+        async function executeHunterTask() {
+            const keywords = document.getElementById('search-keywords').value;
+            const source = document.getElementById('search-source').value;
+            const limit = document.getElementById('search-limit').value;
+
+            if (!keywords.trim()) {
+                showError('hunter-response', '请输入搜索关键词');
+                return;
+            }
+
+            showLoading('hunter-loading');
+
+            try {
+                const response = await fetch('/api/v1/papers/search', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify({
+                        keywords: keywords,
+                        source: source,
+                        limit: parseInt(limit)
+                    })
+                });
+
+                const data = await response.json();
+
+                if (response.ok) {
+                    showSuccess('hunter-response', `找到 ${data.papers?.length || 0} 篇相关论文`, data);
+                } else {
+                    showError('hunter-response', data.detail || '搜索失败');
+                }
+            } catch (error) {
+                showError('hunter-response', `网络错误: ${error.message}`);
+            } finally {
+                hideLoading('hunter-loading');
+            }
+        }
+
+        // 执行Miner任务
+        async function executeMinerTask() {
+            const paperUrl = document.getElementById('paper-url').value;
+            const analysisType = document.getElementById('analysis-type').value;
+
+            if (!paperUrl.trim()) {
+                showError('miner-response', '请输入论文URL或上传PDF文件');
+                return;
+            }
+
+            showLoading('miner-loading');
+
+            try {
+                const response = await fetch('/api/v1/analysis/analyze', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify({
+                        paper_url: paperUrl,
+                        analysis_type: analysisType
+                    })
+                });
+
+                const data = await response.json();
+
+                if (response.ok) {
+                    showSuccess('miner-response', '论文分析完成', data);
+                } else {
+                    showError('miner-response', data.detail || '分析失败');
+                }
+            } catch (error) {
+                showError('miner-response', `网络错误: ${error.message}`);
+            } finally {
+                hideLoading('miner-loading');
+            }
+        }
+
+        // 执行Coach任务
+        async function executeCoachTask() {
+            const text = document.getElementById('writing-text').value;
+            const style = document.getElementById('writing-style').value;
+            const task = document.getElementById('writing-task').value;
+
+            if (!text.trim()) {
+                showError('coach-response', '请输入需要处理的文本');
+                return;
+            }
+
+            showLoading('coach-loading');
+
+            try {
+                const response = await fetch('/api/v1/writing/coach', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify({
+                        text: text,
+                        style: style,
+                        task: task
+                    })
+                });
+
+                const data = await response.json();
+
+                if (response.ok) {
+                    showSuccess('coach-response', '文本处理完成', data);
+                } else {
+                    showError('coach-response', data.detail || '处理失败');
+                }
+            } catch (error) {
+                showError('coach-response', `网络错误: ${error.message}`);
+            } finally {
+                hideLoading('coach-loading');
+            }
+        }
+
+        // 执行Validator任务
+        async function executeValidatorTask() {
+            const citation = document.getElementById('citation-input').value;
+            const format = document.getElementById('citation-format').value;
+
+            if (!citation.trim()) {
+                showError('validator-response', '请输入引用信息');
+                return;
+            }
+
+            showLoading('validator-loading');
+
+            try {
+                const response = await fetch('/api/v1/citations/validate', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify({
+                        citation: citation,
+                        format: format
+                    })
+                });
+
+                const data = await response.json();
+
+                if (response.ok) {
+                    showSuccess('validator-response', '引用校验完成', data);
+                } else {
+                    showError('validator-response', data.detail || '校验失败');
+                }
+            } catch (error) {
+                showError('validator-response', `网络错误: ${error.message}`);
+            } finally {
+                hideLoading('validator-loading');
+            }
+        }
+
+        // 执行完整工作流
+        async function executeWorkflow() {
+            const keywords = document.getElementById('workflow-keywords').value;
+            const limit = document.getElementById('workflow-limit').value;
+            const analysisType = document.getElementById('workflow-analysis-type').value;
+            const citationFormat = document.getElementById('workflow-citation-format').value;
+            const generateReport = document.getElementById('workflow-generate-report').checked;
+
+            if (!keywords.trim()) {
+                showError('workflow-response', '请输入研究关键词');
+                return;
+            }
+
+            showLoading('workflow-loading');
+
+            try {
+                const response = await fetch('/api/v1/workflow/complete', {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify({
+                        keywords: keywords,
+                        limit: parseInt(limit),
+                        analysis_type: analysisType,
+                        citation_format: citationFormat,
+                        writing_task: generateReport ? 'improve' : null
+                    })
+                });
+
+                const data = await response.json();
+
+                if (response.ok) {
+                    showSuccess('workflow-response', '🎉 工作流执行完成!', data);
+                } else {
+                    showError('workflow-response', data.detail || '工作流执行失败');
+                }
+            } catch (error) {
+                showError('workflow-response', `网络错误: ${error.message}`);
+            } finally {
+                hideLoading('workflow-loading');
+            }
+        }
+
+        // 设置文件上传
+        function setupFileUpload() {
+            const uploadArea = document.getElementById('paper-upload-area');
+            const fileInput = document.getElementById('paper-file');
+
+            uploadArea.addEventListener('click', () => fileInput.click());
+
+            uploadArea.addEventListener('dragover', (e) => {
+                e.preventDefault();
+                uploadArea.classList.add('dragover');
+            });
+
+            uploadArea.addEventListener('dragleave', () => {
+                uploadArea.classList.remove('dragover');
+            });
+
+            uploadArea.addEventListener('drop', (e) => {
+                e.preventDefault();
+                uploadArea.classList.remove('dragover');
+
+                const files = e.dataTransfer.files;
+                if (files.length > 0 && files[0].type === 'application/pdf') {
+                    handleFileUpload(files[0]);
+                }
+            });
+
+            fileInput.addEventListener('change', (e) => {
+                if (e.target.files.length > 0) {
+                    handleFileUpload(e.target.files[0]);
+                }
+            });
+        }
+
+        // 处理文件上传
+        async function handleFileUpload(file) {
+            const formData = new FormData();
+            formData.append('file', file);
+
+            showLoading('miner-loading');
+
+            try {
+                // 使用新的 PDF 解析端点
+                const response = await fetch('/api/v1/analysis/upload-pdf', {
+                    method: 'POST',
+                    body: formData
+                });
+
+                const data = await response.json();
+
+                if (response.ok) {
+                    // 自动填充 URL 字段
+                    document.getElementById('paper-url').value = data.file_path || '';
+
+                    // 显示解析结果
+                    const uploadInfo = `
+                        <div class="paper-card">
+                            <h4>📄 ${data.title || file.name}</h4>
+                            <div class="authors">
+                                👥 作者: ${data.authors ? data.authors.join(', ') : '未知'}
+                            </div>
+                            <div class="abstract">
+                                📝 ${data.abstract || '无摘要'}
+                            </div>
+                            <div class="meta">
+                                <span>📄 ${data.page_count || 0} 页</span>
+                                <span>📊 ${data.word_count || 0} 词</span>
+                                <span>✅ 已解析</span>
+                            </div>
+                        </div>
+                    `;
+
+                    showSuccess('miner-response', '✅ PDF 文件上传并解析成功!现在可以选择分析类型并开始分析。', {
+                        upload_info: uploadInfo
+                    });
+                } else {
+                    showError('miner-response', data.detail || '上传失败');
+                }
+            } catch (error) {
+                showError('miner-response', `上传错误: ${error.message}`);
+            } finally {
+                hideLoading('miner-loading');
+            }
+        }
+
+        // 显示加载状态
+        function showLoading(loadingId) {
+            document.getElementById(loadingId).classList.add('active');
+        }
+
+        // 隐藏加载状态
+        function hideLoading(loadingId) {
+            document.getElementById(loadingId).classList.remove('active');
+        }
+
+        // 显示成功消息
+        function showSuccess(responseId, message, data = null) {
+            const responseDiv = document.getElementById(responseId);
+
+            let content = `<div class="success">✅ ${message}</div>`;
+
+            if (data) {
+                // 工作流结果
+                if (data.steps && Array.isArray(data.steps)) {
+                    content += formatWorkflowResult(data);
+                }
+                // PDF 上传信息
+                else if (data.upload_info) {
+                    content += data.upload_info;
+                }
+                // 论文搜索结果
+                else if (data.papers && Array.isArray(data.papers)) {
+                    content += formatPaperResults(data);
+                }
+                // 论文分析结果
+                else if (data.analysis) {
+                    content += formatAnalysisResult(data);
+                }
+                // 写作助手结果
+                else if (data.result && typeof data.result === 'string') {
+                    content += formatWritingResult(data);
+                }
+                // 引用校验结果
+                else if (data.formatted_citation) {
+                    content += formatCitationResult(data);
+                }
+                // 其他类型的数据用格式化的 JSON 展示
+                else {
+                    const jsonStr = JSON.stringify(data, null, 2);
+                    const jsonId = 'json-' + Date.now();
+                    content += `
+                        <div style="position: relative;">
+                            <button class="copy-btn" onclick="copyToClipboard('${jsonId}', event)">📋 复制</button>
+                            <pre id="${jsonId}">${syntaxHighlight(jsonStr)}</pre>
+                        </div>
+                    `;
+                }
+            }
+
+            responseDiv.innerHTML = content;
+        }
+
+        // 格式化论文搜索结果
+        function formatPaperResults(data) {
+            let html = `
+                <div class="result-header">
+                    <span>📚 搜索结果</span>
+                    <span class="result-stats">共找到 ${data.papers.length} 篇论文</span>
+                </div>
+            `;
+
+            data.papers.forEach((paper, index) => {
+                html += `
+                    <div class="paper-card">
+                        <h4>${index + 1}. ${escapeHtml(paper.title)}</h4>
+                        <div class="authors">
+                            👥 作者: ${paper.authors.join(', ')}
+                        </div>
+                        <div class="abstract">
+                            📝 ${escapeHtml(paper.abstract)}
+                        </div>
+                        <div class="meta">
+                            <span>📅 ${paper.published_date}</span>
+                            <span>🔗 <a href="${paper.url}" target="_blank">查看原文</a></span>
+                            ${paper.pdf_url ? `<span>📄 <a href="${paper.pdf_url}" target="_blank">下载PDF</a></span>` : ''}
+                            <span>🆔 ${paper.id}</span>
+                        </div>
+                    </div>
+                `;
+            });
+
+            return html;
+        }
+
+        // 格式化论文分析结果
+        function formatAnalysisResult(data) {
+            let html = `
+                <div class="result-header">
+                    <span>🧠 论文分析</span>
+                    <span class="result-stats">${data.analysis_type}</span>
+                </div>
+            `;
+
+            if (data.paper_info) {
+                html += `
+                    <div class="paper-card">
+                        <h4>${escapeHtml(data.paper_info.title)}</h4>
+                        <div class="authors">
+                            👥 作者: ${data.paper_info.authors.join(', ')}
+                        </div>
+                        <div class="meta">
+                            <span>📅 ${data.paper_info.published_date}</span>
+                            <span>🔗 <a href="${data.paper_info.url}" target="_blank">查看原文</a></span>
+                            <span>🏷️ ${data.paper_info.categories.join(', ')}</span>
+                        </div>
+                    </div>
+                `;
+            }
+
+            const analysisId = 'analysis-' + Date.now();
+            html += '<div class="paper-card">';
+            html += '<h4>📊 分析结果 <button class="copy-btn" onclick="copyToClipboard(\'' + analysisId + '\', event)" style="float: right;">📋 复制</button></h4>';
+            html += '<div class="markdown-content" id="' + analysisId + '">';
+            html += renderMarkdown(data.analysis);
+            html += '</div>';
+            html += '</div>';
+
+            return html;
+        }
+
+        // 改进的 Markdown 渲染器
+        function renderMarkdown(text) {
+            if (!text) return '';
+
+            // 按行处理
+            const lines = text.split('\n');
+            let html = '';
+            let inList = false;
+            let listItems = [];
+
+            for (let i = 0; i < lines.length; i++) {
+                let line = lines[i];
+
+                // 跳过空行
+                if (!line.trim()) {
+                    if (inList) {
+                        html += '<ul style="list-style-type: disc; padding-left: 25px; margin: 15px 0;">' + listItems.join('') + '</ul>';
+                        listItems = [];
+                        inList = false;
+                    }
+                    html += '<br/>';
+                    continue;
+                }
+
+                // 标题 (必须在行首) - 从最长的开始匹配
+                const h4Match = line.match(/^####\s+(.+)$/);
+                if (h4Match) {
+                    if (inList) {
+                        html += '<ul style="list-style-type: disc; padding-left: 25px; margin: 15px 0;">' + listItems.join('') + '</ul>';
+                        listItems = [];
+                        inList = false;
+                    }
+                    html += '<h4 style="color: #667eea; margin: 15px 0 8px 0; font-size: 1.05rem; font-weight: 600;">' + formatInlineMarkdown(h4Match[1]) + '</h4>';
+                    continue;
+                }
+
+                const h3Match = line.match(/^###\s+(.+)$/);
+                if (h3Match) {
+                    if (inList) {
+                        html += '<ul style="list-style-type: disc; padding-left: 25px; margin: 15px 0;">' + listItems.join('') + '</ul>';
+                        listItems = [];
+                        inList = false;
+                    }
+                    html += '<h3 style="color: #667eea; margin: 20px 0 10px 0; font-size: 1.1rem; font-weight: 600;">' + formatInlineMarkdown(h3Match[1]) + '</h3>';
+                    continue;
+                }
+
+                const h2Match = line.match(/^##\s+(.+)$/);
+                if (h2Match) {
+                    if (inList) {
+                        html += '<ul style="list-style-type: disc; padding-left: 25px; margin: 15px 0;">' + listItems.join('') + '</ul>';
+                        listItems = [];
+                        inList = false;
+                    }
+                    html += '<h2 style="color: #667eea; margin: 25px 0 15px 0; font-size: 1.3rem; font-weight: 600;">' + formatInlineMarkdown(h2Match[1]) + '</h2>';
+                    continue;
+                }
+
+                const h1Match = line.match(/^#\s+(.+)$/);
+                if (h1Match) {
+                    if (inList) {
+                        html += '<ul style="list-style-type: disc; padding-left: 25px; margin: 15px 0;">' + listItems.join('') + '</ul>';
+                        listItems = [];
+                        inList = false;
+                    }
+                    html += '<h1 style="color: #667eea; margin: 30px 0 20px 0; font-size: 1.5rem; font-weight: 600;">' + formatInlineMarkdown(h1Match[1]) + '</h1>';
+                    continue;
+                }
+
+                // 有序列表
+                const orderedMatch = line.match(/^(\d+)\.\s+(.+)$/);
+                if (orderedMatch) {
+                    const content = orderedMatch[2];
+                    const formatted = formatInlineMarkdown(content);
+                    listItems.push(`<li style="margin: 10px 0; line-height: 1.8;">${formatted}</li>`);
+                    inList = true;
+                    continue;
+                }
+
+                // 无序列表
+                const unorderedMatch = line.match(/^[-*]\s+(.+)$/);
+                if (unorderedMatch) {
+                    const content = unorderedMatch[1];
+                    const formatted = formatInlineMarkdown(content);
+                    listItems.push(`<li style="margin: 10px 0; line-height: 1.8;">${formatted}</li>`);
+                    inList = true;
+                    continue;
+                }
+
+                // 普通段落
+                if (inList) {
+                    html += '<ul style="list-style-type: disc; padding-left: 25px; margin: 15px 0;">' + listItems.join('') + '</ul>';
+                    listItems = [];
+                    inList = false;
+                }
+
+                const formatted = formatInlineMarkdown(line);
+                html += `<p style="margin: 12px 0; line-height: 1.8; color: #444;">${formatted}</p>`;
+            }
+
+            // 处理未闭合的列表
+            if (inList) {
+                html += '<ul style="list-style-type: disc; padding-left: 25px; margin: 15px 0;">' + listItems.join('') + '</ul>';
+            }
+
+            return html;
+        }
+
+        // 格式化行内 Markdown 元素
+        function formatInlineMarkdown(text) {
+            // 转义 HTML
+            text = escapeHtml(text);
+
+            // 粗体 **text**
+            text = text.replace(/\*\*(.+?)\*\*/g, '<strong style="color: #333; font-weight: 600;">$1</strong>');
+
+            // 斜体 *text*
+            text = text.replace(/\*(.+?)\*/g, '<em>$1</em>');
+
+            // 行内代码 `code`
+            text = text.replace(/`([^`]+)`/g, '<code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-family: monospace; color: #e83e8c; font-size: 0.9em;">$1</code>');
+
+            // 链接 [text](url)
+            text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" style="color: #667eea; text-decoration: none; border-bottom: 1px solid #667eea;">$1</a>');
+
+            return text;
+        }
+
+        // 格式化写作助手结果
+        function formatWritingResult(data) {
+            let html = `
+                <div class="result-header">
+                    <span>✍️ 写作助手</span>
+                    <span class="result-stats">${data.task} - ${data.style}</span>
+                </div>
+            `;
+
+            if (data.original) {
+                html += `
+                    <div class="paper-card">
+                        <h4>📝 原文</h4>
+                        <div class="abstract" style="background: #f8f9fa; padding: 15px; border-radius: 6px;">
+                            ${escapeHtml(data.original)}
+                        </div>
+                    </div>
+                `;
+            }
+
+            const resultId = 'writing-result-' + Date.now();
+            html += '<div class="paper-card">';
+            html += '<h4>✨ 处理结果 <button class="copy-btn" onclick="copyToClipboard(\'' + resultId + '\', event)" style="float: right;">📋 复制</button></h4>';
+            html += '<div class="markdown-content" id="' + resultId + '">';
+            html += renderMarkdown(data.result);
+            html += '</div>';
+            html += '</div>';
+
+            return html;
+        }
+
+        // 格式化引用校验结果
+        function formatCitationResult(data) {
+
+            const jsonId = 'citation-' + Date.now();
+            let html = `
+                <div class="result-header">
+                    <span>🔎 引用校验</span>
+                    <span class="result-stats">${data.format.toUpperCase()} ${data.verified ? '✅ 已验证' : '⚠️ 未验证'}</span>
+                </div>
+            `;
+
+            if (data.metadata) {
+                const meta = data.metadata;
+
+                // 处理标题
+                let title = 'N/A';
+                if (meta.title) {
+                    title = Array.isArray(meta.title) ? meta.title[0] : meta.title;
+                }
+
+                // 处理作者
+                let authors = 'N/A';
+                if (meta.authors) {
+                    if (Array.isArray(meta.authors)) {
+                        authors = meta.authors.join(', ');
+                    } else {
+                        authors = meta.authors;
+                    }
+                }
+
+                // 处理年份
+                let year = 'N/A';
+                if (meta.year) {
+                    year = meta.year;
+                } else if (meta.published && meta.published['date-parts']) {
+                    year = meta.published['date-parts'][0][0];
+                }
+
+                // 处理期刊
+                let journal = 'N/A';
+                if (meta.journal) {
+                    journal = meta.journal;
+                } else if (meta['container-title']) {
+                    journal = Array.isArray(meta['container-title']) ? meta['container-title'][0] : meta['container-title'];
+                }
+
+                html += `
+                    <div class="paper-card">
+                        <h4>📄 论文信息</h4>
+                        <div class="authors">
+                            📖 标题: ${escapeHtml(title)}
+                        </div>
+                        <div class="authors">
+                            👥 作者: ${escapeHtml(authors)}
+                        </div>
+                        <div class="meta">
+                            <span>📅 ${year}</span>
+                            <span>📚 ${escapeHtml(journal)}</span>
+                            ${meta.arxiv_id ? `<span>🔗 arXiv:${meta.arxiv_id}</span>` : ''}
+                            ${meta.doi ? `<span>🔗 DOI:${meta.doi}</span>` : ''}
+                        </div>
+                    </div>
+                `;
+            }
+
+            html += `
+                <div class="paper-card">
+                    <h4>📋 格式化引用 <button class="copy-btn" onclick="copyToClipboard('${jsonId}', event)" style="float: right;">📋 复制</button></h4>
+                    <pre id="${jsonId}" style="background: white; padding: 15px; border-radius: 6px; border-left: 4px solid #667eea; overflow-x: auto;">${escapeHtml(data.formatted_citation)}</pre>
+                </div>
+            `;
+
+            if (data.warnings && data.warnings.length > 0) {
+                html += `
+                    <div class="error">
+                        ⚠️ ${data.warnings.join('; ')}
+                    </div>
+                `;
+            }
+
+            return html;
+        }
+
+        // 格式化工作流结果
+        function formatWorkflowResult(data) {
+            let html = `
+                <div class="result-header">
+                    <span>🔄 工作流执行结果</span>
+                    <span class="result-stats">${data.status === 'completed' ? '✅ 完成' : '⚠️ 部分完成'}</span>
+                </div>
+            `;
+
+            // 显示摘要
+            if (data.summary) {
+                html += `
+                    <div class="paper-card">
+                        <h4>📊 执行摘要</h4>
+                        <div class="meta">
+                            <span>📚 找到 ${data.summary.total_papers} 篇论文</span>
+                            <span>🧠 分析 ${data.summary.analyzed_papers} 篇</span>
+                            <span>📝 生成 ${data.summary.generated_citations} 条引用</span>
+                        </div>
+                        <p style="margin-top: 10px; color: #666;">关键词: ${data.summary.keywords}</p>
+                    </div>
+                `;
+            }
+
+            // 显示各步骤结果
+            if (data.steps && data.steps.length > 0) {
+                data.steps.forEach((step, index) => {
+                    const statusIcon = step.status === 'completed' ? '✅' : step.status === 'failed' ? '❌' : '⏳';
+
+                    html += `
+                        <div class="paper-card">
+                            <h4>${statusIcon} 步骤 ${step.step}: ${step.name}</h4>
+                    `;
+
+                    if (step.status === 'completed' && step.result) {
+                        // 论文搜索结果
+                        if (step.result.papers) {
+                            html += `<p>找到 ${step.result.total_found} 篇论文</p>`;
+                            step.result.papers.slice(0, 3).forEach((paper, i) => {
+                                html += `
+                                    <div style="margin: 10px 0; padding: 10px; background: #f8f9fa; border-radius: 5px;">
+                                        <strong>${i + 1}. ${escapeHtml(paper.title)}</strong><br>
+                                        <small>作者: ${paper.authors.slice(0, 3).join(', ')}${paper.authors.length > 3 ? ' et al.' : ''}</small>
+                                    </div>
+                                `;
+                            });
+                        }
+
+                        // 分析结果
+                        if (step.result.analyses) {
+                            html += `<p>完成 ${step.result.total_analyzed} 篇论文分析</p>`;
+                            step.result.analyses.forEach((analysis, i) => {
+                                html += `
+                                    <div style="margin: 10px 0; padding: 10px; background: #f0f7ff; border-radius: 5px;">
+                                        <strong>${i + 1}. ${escapeHtml(analysis.title)}</strong>
+                                        <div class="markdown-content" style="margin-top: 10px;">
+                                            ${renderMarkdown(analysis.analysis.substring(0, 500) + '...')}
+                                        </div>
+                                    </div>
+                                `;
+                            });
+                        }
+
+                        // 引用结果
+                        if (step.result.citations) {
+                            html += `<p>生成 ${step.result.total_citations} 条引用</p>`;
+                            const citationId = 'citations-' + Date.now();
+                            html += `<button class="copy-btn" onclick="copyToClipboard('${citationId}', event)">📋 复制全部引用</button>`;
+                            html += `<div id="${citationId}" style="margin-top: 10px;">`;
+                            step.result.citations.forEach((citation, i) => {
+                                html += `<p style="margin: 5px 0;">${i + 1}. ${escapeHtml(citation.formatted_citation)}</p>`;
+                            });
+                            html += '</div>';
+                        }
+
+                        // 报告结果
+                        if (step.result.report) {
+                            const reportId = 'report-' + Date.now();
+                            html += `<button class="copy-btn" onclick="copyToClipboard('${reportId}', event)">📋 复制报告</button>`;
+                            html += `<div class="markdown-content" id="${reportId}" style="margin-top: 10px;">`;
+                            html += renderMarkdown(step.result.report);
+                            html += '</div>';
+                        }
+                    } else if (step.status === 'failed') {
+                        html += `<p style="color: #f44336;">错误: ${step.error}</p>`;
+                    }
+
+                    html += '</div>';
+                });
+            }
+
+            return html;
+        }
+
+        // HTML 转义函数
+        function escapeHtml(text) {
+            const div = document.createElement('div');
+            div.textContent = text;
+            return div.innerHTML;
+        }
+
+        // JSON 语法高亮
+        function syntaxHighlight(json) {
+            json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+            return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
+                let cls = 'json-number';
+                if (/^"/.test(match)) {
+                    if (/:$/.test(match)) {
+                        cls = 'json-key';
+                    } else {
+                        cls = 'json-string';
+                    }
+                } else if (/true|false/.test(match)) {
+                    cls = 'json-boolean';
+                } else if (/null/.test(match)) {
+                    cls = 'json-null';
+                }
+                return '<span class="' + cls + '">' + match + '</span>';
+            });
+        }
+
+        // 复制到剪贴板
+        function copyToClipboard(elementId, event) {
+            // 阻止事件冒泡
+            if (event) {
+                event.preventDefault();
+                event.stopPropagation();
+            }
+
+            const element = document.getElementById(elementId);
+            if (!element) {
+                return;
+            }
+
+            // 获取纯文本内容
+            const text = element.innerText || element.textContent;
+
+            navigator.clipboard.writeText(text).then(() => {
+                // 显示复制成功提示
+                const btn = event ? event.target : null;
+                if (btn) {
+                    const originalText = btn.textContent;
+                    btn.textContent = '✅ 已复制';
+                    btn.style.background = '#4caf50';
+                    btn.style.color = 'white';
+                    btn.style.borderColor = '#4caf50';
+
+                    setTimeout(() => {
+                        btn.textContent = originalText;
+                        btn.style.background = '';
+                        btn.style.color = '';
+                        btn.style.borderColor = '';
+                    }, 2000);
+                }
+                console.log('已复制 ' + text.length + ' 个字符');
+            }).catch(err => {
+                console.error('复制失败:', err);
+                alert('复制失败,请手动选择文本复制');
+            });
+        }
+
+        // 显示错误消息
+        function showError(responseId, message) {
+            const responseDiv = document.getElementById(responseId);
+            responseDiv.innerHTML = `
+                <div class="error">
+                    ❌ ${message}
+                </div>
+            `;
+        }
+    </script>
+</body>
+
+</html>

+ 473 - 0
Co-creation-projects/Apricity-InnocoreAI/frontend/static/css/style.css

@@ -0,0 +1,473 @@
+/* 研创·智核 - 主样式文件 */
+
+:root {
+    --primary-color: #2563eb;
+    --secondary-color: #7c3aed;
+    --success-color: #10b981;
+    --warning-color: #f59e0b;
+    --danger-color: #ef4444;
+    --dark-color: #1f2937;
+    --light-color: #f9fafb;
+    --border-color: #e5e7eb;
+    --text-muted: #6b7280;
+    --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
+    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
+}
+
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+body {
+    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+    line-height: 1.6;
+    color: var(--dark-color);
+    background-color: var(--light-color);
+}
+
+/* 布局 */
+.container {
+    max-width: 1200px;
+    margin: 0 auto;
+    padding: 0 20px;
+}
+
+.main-layout {
+    display: flex;
+    min-height: 100vh;
+}
+
+.sidebar {
+    width: 250px;
+    background: white;
+    border-right: 1px solid var(--border-color);
+    box-shadow: var(--shadow-sm);
+}
+
+.content {
+    flex: 1;
+    padding: 20px;
+}
+
+/* 导航 */
+.navbar {
+    background: white;
+    border-bottom: 1px solid var(--border-color);
+    padding: 1rem 0;
+    box-shadow: var(--shadow-sm);
+}
+
+.navbar-brand {
+    font-size: 1.5rem;
+    font-weight: 700;
+    color: var(--primary-color);
+    text-decoration: none;
+}
+
+.navbar-nav {
+    display: flex;
+    list-style: none;
+    gap: 2rem;
+}
+
+.nav-link {
+    color: var(--dark-color);
+    text-decoration: none;
+    padding: 0.5rem 1rem;
+    border-radius: 0.375rem;
+    transition: all 0.2s;
+}
+
+.nav-link:hover,
+.nav-link.active {
+    background-color: var(--primary-color);
+    color: white;
+}
+
+/* 侧边栏 */
+.sidebar-nav {
+    padding: 1rem 0;
+}
+
+.sidebar-item {
+    display: block;
+    padding: 0.75rem 1.5rem;
+    color: var(--dark-color);
+    text-decoration: none;
+    transition: all 0.2s;
+    border-left: 3px solid transparent;
+}
+
+.sidebar-item:hover,
+.sidebar-item.active {
+    background-color: var(--light-color);
+    border-left-color: var(--primary-color);
+    color: var(--primary-color);
+}
+
+/* 卡片 */
+.card {
+    background: white;
+    border-radius: 0.5rem;
+    box-shadow: var(--shadow-md);
+    overflow: hidden;
+    margin-bottom: 1.5rem;
+}
+
+.card-header {
+    padding: 1rem 1.5rem;
+    border-bottom: 1px solid var(--border-color);
+    background: var(--light-color);
+}
+
+.card-body {
+    padding: 1.5rem;
+}
+
+.card-title {
+    font-size: 1.25rem;
+    font-weight: 600;
+    margin-bottom: 0.5rem;
+}
+
+.card-text {
+    color: var(--text-muted);
+    margin-bottom: 1rem;
+}
+
+/* 按钮 */
+.btn {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    padding: 0.5rem 1rem;
+    border: 1px solid transparent;
+    border-radius: 0.375rem;
+    font-size: 0.875rem;
+    font-weight: 500;
+    text-decoration: none;
+    cursor: pointer;
+    transition: all 0.2s;
+    gap: 0.5rem;
+}
+
+.btn-primary {
+    background-color: var(--primary-color);
+    color: white;
+    border-color: var(--primary-color);
+}
+
+.btn-primary:hover {
+    background-color: #1d4ed8;
+    border-color: #1d4ed8;
+}
+
+.btn-secondary {
+    background-color: var(--secondary-color);
+    color: white;
+    border-color: var(--secondary-color);
+}
+
+.btn-secondary:hover {
+    background-color: #6d28d9;
+    border-color: #6d28d9;
+}
+
+.btn-success {
+    background-color: var(--success-color);
+    color: white;
+    border-color: var(--success-color);
+}
+
+.btn-danger {
+    background-color: var(--danger-color);
+    color: white;
+    border-color: var(--danger-color);
+}
+
+.btn-outline {
+    background-color: transparent;
+    border-color: var(--border-color);
+    color: var(--dark-color);
+}
+
+.btn-outline:hover {
+    background-color: var(--light-color);
+}
+
+.btn-sm {
+    padding: 0.25rem 0.5rem;
+    font-size: 0.75rem;
+}
+
+.btn-lg {
+    padding: 0.75rem 1.5rem;
+    font-size: 1rem;
+}
+
+/* 表单 */
+.form-group {
+    margin-bottom: 1rem;
+}
+
+.form-label {
+    display: block;
+    margin-bottom: 0.5rem;
+    font-weight: 500;
+}
+
+.form-control {
+    width: 100%;
+    padding: 0.5rem 0.75rem;
+    border: 1px solid var(--border-color);
+    border-radius: 0.375rem;
+    font-size: 0.875rem;
+    transition: border-color 0.2s;
+}
+
+.form-control:focus {
+    outline: none;
+    border-color: var(--primary-color);
+    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
+}
+
+.form-select {
+    width: 100%;
+    padding: 0.5rem 0.75rem;
+    border: 1px solid var(--border-color);
+    border-radius: 0.375rem;
+    font-size: 0.875rem;
+    background: white;
+}
+
+.form-textarea {
+    min-height: 100px;
+    resize: vertical;
+}
+
+/* 表格 */
+.table {
+    width: 100%;
+    border-collapse: collapse;
+    background: white;
+}
+
+.table th,
+.table td {
+    padding: 0.75rem;
+    text-align: left;
+    border-bottom: 1px solid var(--border-color);
+}
+
+.table th {
+    background-color: var(--light-color);
+    font-weight: 600;
+}
+
+.table tbody tr:hover {
+    background-color: rgba(37, 99, 235, 0.05);
+}
+
+/* 徽章 */
+.badge {
+    display: inline-flex;
+    align-items: center;
+    padding: 0.25rem 0.5rem;
+    font-size: 0.75rem;
+    font-weight: 500;
+    border-radius: 9999px;
+}
+
+.badge-primary {
+    background-color: rgba(37, 99, 235, 0.1);
+    color: var(--primary-color);
+}
+
+.badge-success {
+    background-color: rgba(16, 185, 129, 0.1);
+    color: var(--success-color);
+}
+
+.badge-warning {
+    background-color: rgba(245, 158, 11, 0.1);
+    color: var(--warning-color);
+}
+
+.badge-danger {
+    background-color: rgba(239, 68, 68, 0.1);
+    color: var(--danger-color);
+}
+
+/* 进度条 */
+.progress {
+    width: 100%;
+    height: 0.5rem;
+    background-color: var(--border-color);
+    border-radius: 9999px;
+    overflow: hidden;
+}
+
+.progress-bar {
+    height: 100%;
+    background-color: var(--primary-color);
+    transition: width 0.3s ease;
+}
+
+/* 模态框 */
+.modal {
+    display: none;
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.5);
+    z-index: 1000;
+}
+
+.modal.show {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.modal-content {
+    background: white;
+    border-radius: 0.5rem;
+    box-shadow: var(--shadow-lg);
+    max-width: 500px;
+    width: 90%;
+    max-height: 90vh;
+    overflow-y: auto;
+}
+
+.modal-header {
+    padding: 1rem 1.5rem;
+    border-bottom: 1px solid var(--border-color);
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+}
+
+.modal-title {
+    font-size: 1.25rem;
+    font-weight: 600;
+}
+
+.modal-body {
+    padding: 1.5rem;
+}
+
+.modal-footer {
+    padding: 1rem 1.5rem;
+    border-top: 1px solid var(--border-color);
+    display: flex;
+    gap: 0.5rem;
+    justify-content: flex-end;
+}
+
+/* 工具提示 */
+.tooltip {
+    position: relative;
+}
+
+.tooltip::after {
+    content: attr(data-tooltip);
+    position: absolute;
+    bottom: 100%;
+    left: 50%;
+    transform: translateX(-50%);
+    background-color: var(--dark-color);
+    color: white;
+    padding: 0.25rem 0.5rem;
+    border-radius: 0.25rem;
+    font-size: 0.75rem;
+    white-space: nowrap;
+    opacity: 0;
+    pointer-events: none;
+    transition: opacity 0.2s;
+}
+
+.tooltip:hover::after {
+    opacity: 1;
+}
+
+/* 加载动画 */
+.spinner {
+    width: 20px;
+    height: 20px;
+    border: 2px solid var(--border-color);
+    border-top: 2px solid var(--primary-color);
+    border-radius: 50%;
+    animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+    0% { transform: rotate(0deg); }
+    100% { transform: rotate(360deg); }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+    .main-layout {
+        flex-direction: column;
+    }
+    
+    .sidebar {
+        width: 100%;
+        order: 2;
+    }
+    
+    .content {
+        order: 1;
+    }
+    
+    .navbar-nav {
+        flex-direction: column;
+        gap: 0.5rem;
+    }
+    
+    .container {
+        padding: 0 10px;
+    }
+}
+
+/* 动画 */
+.fade-in {
+    animation: fadeIn 0.3s ease-in;
+}
+
+@keyframes fadeIn {
+    from { opacity: 0; transform: translateY(10px); }
+    to { opacity: 1; transform: translateY(0); }
+}
+
+.slide-in {
+    animation: slideIn 0.3s ease-out;
+}
+
+@keyframes slideIn {
+    from { transform: translateX(-100%); }
+    to { transform: translateX(0); }
+}
+
+/* 自定义滚动条 */
+::-webkit-scrollbar {
+    width: 8px;
+}
+
+::-webkit-scrollbar-track {
+    background: var(--light-color);
+}
+
+::-webkit-scrollbar-thumb {
+    background: var(--border-color);
+    border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+    background: var(--text-muted);
+}

+ 643 - 0
Co-creation-projects/Apricity-InnocoreAI/frontend/static/js/app.js

@@ -0,0 +1,643 @@
+// 研创·智核 - 主应用JavaScript
+
+class InnoCoreApp {
+    constructor() {
+        this.api = new API();
+        this.router = new Router();
+        this.state = new StateManager();
+        this.init();
+    }
+
+    init() {
+        this.setupEventListeners();
+        this.setupRouter();
+        this.checkAuth();
+    }
+
+    setupEventListeners() {
+        // 全局事件监听
+        document.addEventListener('click', (e) => {
+            if (e.target.matches('[data-action]')) {
+                this.handleAction(e.target.dataset.action, e.target);
+            }
+        });
+
+        // 表单提交
+        document.addEventListener('submit', (e) => {
+            if (e.target.matches('.ajax-form')) {
+                e.preventDefault();
+                this.handleFormSubmit(e.target);
+            }
+        });
+
+        // 模态框关闭
+        document.addEventListener('click', (e) => {
+            if (e.target.matches('.modal') || e.target.matches('.modal-close')) {
+                this.closeModal(e.target.closest('.modal'));
+            }
+        });
+    }
+
+    setupRouter() {
+        // 路由配置
+        this.router.addRoute('/', () => this.showDashboard());
+        this.router.addRoute('/papers', () => this.showPapers());
+        this.router.addRoute('/papers/new', () => this.showNewPaper());
+        this.router.addRoute('/papers/:id', (params) => this.showPaperDetail(params.id));
+        this.router.addRoute('/tasks', () => this.showTasks());
+        this.router.addRoute('/tasks/:id', (params) => this.showTaskDetail(params.id));
+        this.router.addRoute('/analysis', () => this.showAnalysis());
+        this.router.addRoute('/writing', () => this.showWriting());
+        this.router.addRoute('/profile', () => this.showProfile());
+    }
+
+    async checkAuth() {
+        const token = localStorage.getItem('token');
+        if (token) {
+            try {
+                const user = await this.api.get('/auth/me');
+                this.state.setUser(user);
+                this.updateUI();
+            } catch (error) {
+                localStorage.removeItem('token');
+                this.showLogin();
+            }
+        } else {
+            this.showLogin();
+        }
+    }
+
+    updateUI() {
+        const user = this.state.getUser();
+        if (user) {
+            document.querySelector('.user-name').textContent = user.username;
+            document.querySelector('.user-email').textContent = user.email;
+        }
+    }
+
+    // 页面显示方法
+    async showDashboard() {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('dashboard');
+        
+        // 加载统计数据
+        const stats = await this.api.get('/dashboard/stats');
+        this.renderDashboardStats(stats);
+        
+        // 加载最近任务
+        const tasks = await this.api.get('/tasks?limit=5');
+        this.renderRecentTasks(tasks);
+    }
+
+    async showPapers() {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('papers');
+        
+        // 加载论文列表
+        const papers = await this.api.get('/papers');
+        this.renderPapersList(papers);
+    }
+
+    async showNewPaper() {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('new-paper');
+        
+        // 初始化表单
+        this.initPaperForm();
+    }
+
+    async showPaperDetail(paperId) {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('paper-detail');
+        
+        // 加载论文详情
+        const paper = await this.api.get(`/papers/${paperId}`);
+        this.renderPaperDetail(paper);
+    }
+
+    async showTasks() {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('tasks');
+        
+        // 加载任务列表
+        const tasks = await this.api.get('/tasks');
+        this.renderTasksList(tasks);
+    }
+
+    async showTaskDetail(taskId) {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('task-detail');
+        
+        // 加载任务详情
+        const task = await this.api.get(`/tasks/${taskId}`);
+        this.renderTaskDetail(task);
+        
+        // 如果任务正在运行,开始轮询状态
+        if (task.status === 'running') {
+            this.startTaskPolling(taskId);
+        }
+    }
+
+    async showAnalysis() {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('analysis');
+        
+        // 加载分析列表
+        const analyses = await this.api.get('/analysis');
+        this.renderAnalysisList(analyses);
+    }
+
+    async showWriting() {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('writing');
+        
+        // 加载写作列表
+        const writings = await this.api.get('/writing');
+        this.renderWritingList(writings);
+    }
+
+    async showProfile() {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('profile');
+        
+        // 加载用户信息
+        const user = await this.api.get('/auth/me');
+        this.renderProfile(user);
+    }
+
+    async showLogin() {
+        const content = document.getElementById('content');
+        content.innerHTML = await this.loadTemplate('login');
+        
+        // 初始化登录表单
+        this.initLoginForm();
+    }
+
+    // 事件处理方法
+    async handleAction(action, element) {
+        switch (action) {
+            case 'logout':
+                await this.logout();
+                break;
+            case 'delete-paper':
+                await this.deletePaper(element.dataset.id);
+                break;
+            case 'delete-task':
+                await this.deleteTask(element.dataset.id);
+                break;
+            case 'cancel-task':
+                await this.cancelTask(element.dataset.id);
+                break;
+            case 'retry-task':
+                await this.retryTask(element.dataset.id);
+                break;
+            case 'export-paper':
+                await this.exportPaper(element.dataset.id, element.dataset.format);
+                break;
+            case 'show-modal':
+                this.showModal(element.dataset.target);
+                break;
+            case 'close-modal':
+                this.closeModal(element.closest('.modal'));
+                break;
+        }
+    }
+
+    async handleFormSubmit(form) {
+        const formData = new FormData(form);
+        const data = Object.fromEntries(formData.entries());
+        
+        try {
+            this.showLoading(form);
+            
+            const endpoint = form.dataset.endpoint;
+            const method = form.dataset.method || 'POST';
+            
+            let result;
+            if (method === 'POST') {
+                result = await this.api.post(endpoint, data);
+            } else if (method === 'PUT') {
+                result = await this.api.put(endpoint, data);
+            }
+            
+            this.showSuccess('操作成功!');
+            
+            // 根据结果跳转
+            if (form.dataset.redirect) {
+                this.router.navigate(form.dataset.redirect);
+            } else if (result.id) {
+                this.router.navigate(`/${form.dataset.resource}/${result.id}`);
+            }
+            
+        } catch (error) {
+            this.showError(error.message);
+        } finally {
+            this.hideLoading(form);
+        }
+    }
+
+    // API调用方法
+    async createLiteratureSearchTask(query, options = {}) {
+        const data = {
+            title: `文献搜索: ${query}`,
+            task_type: 'literature_search',
+            parameters: {
+                query,
+                max_papers: options.maxPapers || 20,
+                year_range: options.yearRange,
+                venues: options.venues || []
+            }
+        };
+        
+        const task = await this.api.post('/tasks', data);
+        this.router.navigate(`/tasks/${task.id}`);
+        return task;
+    }
+
+    async createAnalysisTask(paperIds, analysisType) {
+        const data = {
+            title: `论文分析: ${analysisType}`,
+            task_type: 'analysis',
+            parameters: {
+                paper_ids: paperIds,
+                analysis_type: analysisType
+            }
+        };
+        
+        const task = await this.api.post('/tasks', data);
+        this.router.navigate(`/tasks/${task.id}`);
+        return task;
+    }
+
+    async createWritingTask(paperIds, writingType, outline) {
+        const data = {
+            title: `学术写作: ${writingType}`,
+            task_type: 'writing',
+            parameters: {
+                paper_ids: paperIds,
+                writing_type: writingType,
+                outline: outline
+            }
+        };
+        
+        const task = await this.api.post('/tasks', data);
+        this.router.navigate(`/tasks/${task.id}`);
+        return task;
+    }
+
+    // 渲染方法
+    renderDashboardStats(stats) {
+        const container = document.getElementById('stats-container');
+        container.innerHTML = `
+            <div class="stats-grid">
+                <div class="stat-card">
+                    <h3>${stats.total_papers}</h3>
+                    <p>论文总数</p>
+                </div>
+                <div class="stat-card">
+                    <h3>${stats.total_tasks}</h3>
+                    <p>任务总数</p>
+                </div>
+                <div class="stat-card">
+                    <h3>${stats.total_analyses}</h3>
+                    <p>分析报告</p>
+                </div>
+                <div class="stat-card">
+                    <h3>${stats.total_writings}</h3>
+                    <p>写作文档</p>
+                </div>
+            </div>
+        `;
+    }
+
+    renderPapersList(papers) {
+        const container = document.getElementById('papers-list');
+        container.innerHTML = papers.map(paper => `
+            <div class="paper-card" data-id="${paper.id}">
+                <h3>${paper.title}</h3>
+                <p class="authors">${paper.authors.join(', ')}</p>
+                <p class="abstract">${paper.abstract || '暂无摘要'}</p>
+                <div class="paper-meta">
+                    <span class="badge badge-primary">${paper.publication_year || '未知年份'}</span>
+                    <span class="badge badge-secondary">${paper.journal || '未知期刊'}</span>
+                </div>
+                <div class="paper-actions">
+                    <button class="btn btn-sm btn-primary" data-action="view-paper" data-id="${paper.id}">查看</button>
+                    <button class="btn btn-sm btn-outline" data-action="export-paper" data-id="${paper.id}">导出</button>
+                    <button class="btn btn-sm btn-danger" data-action="delete-paper" data-id="${paper.id}">删除</button>
+                </div>
+            </div>
+        `).join('');
+    }
+
+    renderTasksList(tasks) {
+        const container = document.getElementById('tasks-list');
+        container.innerHTML = tasks.map(task => `
+            <div class="task-card" data-id="${task.id}">
+                <h3>${task.title}</h3>
+                <p class="task-description">${task.description || '暂无描述'}</p>
+                <div class="task-meta">
+                    <span class="badge badge-${this.getStatusClass(task.status)}">${this.getStatusText(task.status)}</span>
+                    <span class="task-type">${this.getTaskTypeText(task.task_type)}</span>
+                </div>
+                <div class="task-progress">
+                    <div class="progress">
+                        <div class="progress-bar" style="width: ${task.progress}%"></div>
+                    </div>
+                    <span class="progress-text">${task.progress}%</span>
+                </div>
+                <div class="task-actions">
+                    <button class="btn btn-sm btn-primary" data-action="view-task" data-id="${task.id}">查看</button>
+                    ${task.status === 'running' ? `<button class="btn btn-sm btn-warning" data-action="cancel-task" data-id="${task.id}">取消</button>` : ''}
+                    ${task.status === 'failed' ? `<button class="btn btn-sm btn-secondary" data-action="retry-task" data-id="${task.id}">重试</button>` : ''}
+                </div>
+            </div>
+        `).join('');
+    }
+
+    // 工具方法
+    getStatusClass(status) {
+        const classes = {
+            'pending': 'warning',
+            'running': 'primary',
+            'completed': 'success',
+            'failed': 'danger'
+        };
+        return classes[status] || 'secondary';
+    }
+
+    getStatusText(status) {
+        const texts = {
+            'pending': '等待中',
+            'running': '运行中',
+            'completed': '已完成',
+            'failed': '失败'
+        };
+        return texts[status] || status;
+    }
+
+    getTaskTypeText(type) {
+        const texts = {
+            'literature_search': '文献搜索',
+            'analysis': '论文分析',
+            'writing': '学术写作'
+        };
+        return texts[type] || type;
+    }
+
+    async loadTemplate(templateName) {
+        try {
+            const response = await fetch(`/templates/${templateName}.html`);
+            return await response.text();
+        } catch (error) {
+            console.error('Failed to load template:', error);
+            return '<div>模板加载失败</div>';
+        }
+    }
+
+    showModal(modalId) {
+        const modal = document.getElementById(modalId);
+        if (modal) {
+            modal.classList.add('show');
+        }
+    }
+
+    closeModal(modal) {
+        if (modal) {
+            modal.classList.remove('show');
+        }
+    }
+
+    showLoading(element) {
+        element.disabled = true;
+        element.innerHTML = '<span class="spinner"></span> 处理中...';
+    }
+
+    hideLoading(element) {
+        element.disabled = false;
+        element.innerHTML = element.dataset.originalText || element.textContent;
+    }
+
+    showSuccess(message) {
+        this.showNotification(message, 'success');
+    }
+
+    showError(message) {
+        this.showNotification(message, 'error');
+    }
+
+    showNotification(message, type = 'info') {
+        const notification = document.createElement('div');
+        notification.className = `notification notification-${type}`;
+        notification.textContent = message;
+        
+        document.body.appendChild(notification);
+        
+        setTimeout(() => {
+            notification.remove();
+        }, 3000);
+    }
+
+    startTaskPolling(taskId) {
+        const poll = async () => {
+            try {
+                const task = await this.api.get(`/tasks/${taskId}`);
+                this.updateTaskStatus(task);
+                
+                if (task.status === 'completed' || task.status === 'failed') {
+                    clearInterval(this.pollingInterval);
+                }
+            } catch (error) {
+                console.error('Task polling error:', error);
+            }
+        };
+        
+        this.pollingInterval = setInterval(poll, 2000);
+    }
+
+    updateTaskStatus(task) {
+        const progressBar = document.querySelector('.progress-bar');
+        const progressText = document.querySelector('.progress-text');
+        const statusBadge = document.querySelector('.task-meta .badge');
+        
+        if (progressBar) progressBar.style.width = `${task.progress}%`;
+        if (progressText) progressText.textContent = `${task.progress}%`;
+        if (statusBadge) {
+            statusBadge.className = `badge badge-${this.getStatusClass(task.status)}`;
+            statusBadge.textContent = this.getStatusText(task.status);
+        }
+    }
+
+    async logout() {
+        localStorage.removeItem('token');
+        this.state.clearUser();
+        this.router.navigate('/login');
+    }
+}
+
+// API类
+class API {
+    constructor() {
+        this.baseURL = '/api/v1';
+    }
+
+    async request(endpoint, options = {}) {
+        const token = localStorage.getItem('token');
+        const url = `${this.baseURL}${endpoint}`;
+        
+        const config = {
+            headers: {
+                'Content-Type': 'application/json',
+                ...(token && { 'Authorization': `Bearer ${token}` }),
+                ...options.headers
+            },
+            ...options
+        };
+
+        const response = await fetch(url, config);
+        
+        if (!response.ok) {
+            const error = await response.json();
+            throw new Error(error.message || '请求失败');
+        }
+
+        return await response.json();
+    }
+
+    get(endpoint) {
+        return this.request(endpoint);
+    }
+
+    post(endpoint, data) {
+        return this.request(endpoint, {
+            method: 'POST',
+            body: JSON.stringify(data)
+        });
+    }
+
+    put(endpoint, data) {
+        return this.request(endpoint, {
+            method: 'PUT',
+            body: JSON.stringify(data)
+        });
+    }
+
+    delete(endpoint) {
+        return this.request(endpoint, {
+            method: 'DELETE'
+        });
+    }
+}
+
+// 路由类
+class Router {
+    constructor() {
+        this.routes = new Map();
+        this.currentPath = window.location.pathname;
+        
+        window.addEventListener('popstate', () => {
+            this.handleRoute();
+        });
+    }
+
+    addRoute(path, handler) {
+        this.routes.set(path, handler);
+    }
+
+    navigate(path) {
+        window.history.pushState({}, '', path);
+        this.currentPath = path;
+        this.handleRoute();
+    }
+
+    handleRoute() {
+        const path = window.location.pathname;
+        
+        for (const [route, handler] of this.routes) {
+            if (this.matchRoute(route, path)) {
+                const params = this.extractParams(route, path);
+                handler(params);
+                return;
+            }
+        }
+        
+        // 404处理
+        this.show404();
+    }
+
+    matchRoute(route, path) {
+        const routeParts = route.split('/');
+        const pathParts = path.split('/');
+        
+        if (routeParts.length !== pathParts.length) {
+            return false;
+        }
+
+        return routeParts.every((part, index) => {
+            return part.startsWith(':') || part === pathParts[index];
+        });
+    }
+
+    extractParams(route, path) {
+        const routeParts = route.split('/');
+        const pathParts = path.split('/');
+        const params = {};
+
+        routeParts.forEach((part, index) => {
+            if (part.startsWith(':')) {
+                const paramName = part.substring(1);
+                params[paramName] = pathParts[index];
+            }
+        });
+
+        return params;
+    }
+
+    show404() {
+        document.getElementById('content').innerHTML = '<h1>页面未找到</h1>';
+    }
+}
+
+// 状态管理类
+class StateManager {
+    constructor() {
+        this.state = {
+            user: null,
+            currentTask: null,
+            notifications: []
+        };
+    }
+
+    setUser(user) {
+        this.state.user = user;
+    }
+
+    getUser() {
+        return this.state.user;
+    }
+
+    clearUser() {
+        this.state.user = null;
+    }
+
+    setCurrentTask(task) {
+        this.state.currentTask = task;
+    }
+
+    getCurrentTask() {
+        return this.state.currentTask;
+    }
+
+    addNotification(notification) {
+        this.state.notifications.push(notification);
+    }
+
+    getNotifications() {
+        return this.state.notifications;
+    }
+}
+
+// 初始化应用
+document.addEventListener('DOMContentLoaded', () => {
+    window.app = new InnoCoreApp();
+});

+ 419 - 0
Co-creation-projects/Apricity-InnocoreAI/frontend/templates/dashboard.html

@@ -0,0 +1,419 @@
+<!-- 仪表板模板 -->
+<div class="dashboard fade-in">
+    <div class="dashboard-header">
+        <h1>研创·智核仪表板</h1>
+        <p>欢迎使用智能科研助手平台</p>
+    </div>
+
+    <!-- 统计卡片 -->
+    <div class="stats-section">
+        <h2>数据概览</h2>
+        <div id="stats-container" class="stats-grid">
+            <!-- 统计数据将通过JavaScript动态加载 -->
+        </div>
+    </div>
+
+    <!-- 快速操作 -->
+    <div class="quick-actions">
+        <h2>快速操作</h2>
+        <div class="action-grid">
+            <div class="action-card" data-action="show-modal" data-target="literature-search-modal">
+                <div class="action-icon">🔍</div>
+                <h3>文献搜索</h3>
+                <p>智能搜索相关文献</p>
+            </div>
+            <div class="action-card" data-action="show-modal" data-target="paper-analysis-modal">
+                <div class="action-icon">📊</div>
+                <h3>论文分析</h3>
+                <p>深度分析论文内容</p>
+            </div>
+            <div class="action-card" data-action="show-modal" data-target="academic-writing-modal">
+                <div class="action-icon">✍️</div>
+                <h3>学术写作</h3>
+                <p>辅助生成学术文档</p>
+            </div>
+            <div class="action-card" data-action="navigate" data-target="/papers/new">
+                <div class="action-icon">📄</div>
+                <h3>添加论文</h3>
+                <p>手动添加论文信息</p>
+            </div>
+        </div>
+    </div>
+
+    <!-- 最近任务 -->
+    <div class="recent-tasks">
+        <h2>最近任务</h2>
+        <div id="recent-tasks-container">
+            <!-- 最近任务将通过JavaScript动态加载 -->
+        </div>
+        <div class="view-all">
+            <a href="/tasks" class="btn btn-outline">查看所有任务</a>
+        </div>
+    </div>
+
+    <!-- 系统状态 -->
+    <div class="system-status">
+        <h2>系统状态</h2>
+        <div class="status-grid">
+            <div class="status-item">
+                <div class="status-indicator status-online"></div>
+                <span>API服务</span>
+                <span class="status-text">在线</span>
+            </div>
+            <div class="status-item">
+                <div class="status-indicator status-online"></div>
+                <span>向量数据库</span>
+                <span class="status-text">正常</span>
+            </div>
+            <div class="status-item">
+                <div class="status-indicator status-online"></div>
+                <span>智能体服务</span>
+                <span class="status-text">运行中</span>
+            </div>
+            <div class="status-item">
+                <div class="status-indicator status-warning"></div>
+                <span>存储空间</span>
+                <span class="status-text">75%</span>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 文献搜索模态框 -->
+<div id="literature-search-modal" class="modal">
+    <div class="modal-content">
+        <div class="modal-header">
+            <h3 class="modal-title">智能文献搜索</h3>
+            <button class="modal-close" data-action="close-modal">&times;</button>
+        </div>
+        <div class="modal-body">
+            <form class="ajax-form" data-endpoint="/tasks" data-method="POST" data-redirect="/tasks/{id}">
+                <div class="form-group">
+                    <label class="form-label">搜索关键词</label>
+                    <input type="text" name="query" class="form-control" placeholder="输入搜索关键词..." required>
+                </div>
+                <div class="form-group">
+                    <label class="form-label">最大论文数量</label>
+                    <select name="max_papers" class="form-select">
+                        <option value="10">10篇</option>
+                        <option value="20" selected>20篇</option>
+                        <option value="50">50篇</option>
+                        <option value="100">100篇</option>
+                    </select>
+                </div>
+                <div class="form-group">
+                    <label class="form-label">发表年份范围</label>
+                    <div class="year-range">
+                        <input type="number" name="year_start" class="form-control" placeholder="起始年份" min="1900" max="2024">
+                        <span>至</span>
+                        <input type="number" name="year_end" class="form-control" placeholder="结束年份" min="1900" max="2024">
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label class="form-label">期刊/会议</label>
+                    <input type="text" name="venues" class="form-control" placeholder="输入期刊或会议名称,多个用逗号分隔">
+                </div>
+                <input type="hidden" name="title" value="文献搜索任务">
+                <input type="hidden" name="task_type" value="literature_search">
+                <input type="hidden" name="priority" value="medium">
+            </form>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="btn btn-outline" data-action="close-modal">取消</button>
+            <button type="submit" form="ajax-form" class="btn btn-primary">开始搜索</button>
+        </div>
+    </div>
+</div>
+
+<!-- 论文分析模态框 -->
+<div id="paper-analysis-modal" class="modal">
+    <div class="modal-content">
+        <div class="modal-header">
+            <h3 class="modal-title">论文分析</h3>
+            <button class="modal-close" data-action="close-modal">&times;</button>
+        </div>
+        <div class="modal-body">
+            <form class="ajax-form" data-endpoint="/tasks" data-method="POST" data-redirect="/tasks/{id}">
+                <div class="form-group">
+                    <label class="form-label">选择论文</label>
+                    <div class="paper-selector">
+                        <!-- 论文选择器将通过JavaScript动态加载 -->
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label class="form-label">分析类型</label>
+                    <select name="analysis_type" class="form-select" required>
+                        <option value="">请选择分析类型</option>
+                        <option value="comprehensive">综合分析</option>
+                        <option value="methodology">方法论分析</option>
+                        <option value="findings">研究发现分析</option>
+                        <option value="gap">研究缺口分析</option>
+                        <option value="trend">趋势分析</option>
+                    </select>
+                </div>
+                <div class="form-group">
+                    <label class="form-label">重点关注领域</label>
+                    <input type="text" name="focus_areas" class="form-control" placeholder="输入重点关注领域,多个用逗号分隔">
+                </div>
+                <input type="hidden" name="title" value="论文分析任务">
+                <input type="hidden" name="task_type" value="analysis">
+                <input type="hidden" name="priority" value="medium">
+            </form>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="btn btn-outline" data-action="close-modal">取消</button>
+            <button type="submit" form="ajax-form" class="btn btn-primary">开始分析</button>
+        </div>
+    </div>
+</div>
+
+<!-- 学术写作模态框 -->
+<div id="academic-writing-modal" class="modal">
+    <div class="modal-content modal-large">
+        <div class="modal-header">
+            <h3 class="modal-title">学术写作助手</h3>
+            <button class="modal-close" data-action="close-modal">&times;</button>
+        </div>
+        <div class="modal-body">
+            <form class="ajax-form" data-endpoint="/tasks" data-method="POST" data-redirect="/tasks/{id}">
+                <div class="form-group">
+                    <label class="form-label">写作类型</label>
+                    <select name="writing_type" class="form-select" required>
+                        <option value="">请选择写作类型</option>
+                        <option value="review">文献综述</option>
+                        <option value="summary">论文总结</option>
+                        <option value="critique">论文评述</option>
+                        <option value="proposal">研究提案</option>
+                    </select>
+                </div>
+                <div class="form-group">
+                    <label class="form-label">参考论文</label>
+                    <div class="paper-selector">
+                        <!-- 论文选择器将通过JavaScript动态加载 -->
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label class="form-label">写作大纲</label>
+                    <textarea name="outline" class="form-control form-textarea" rows="6" placeholder="输入写作大纲,每行一个章节..."></textarea>
+                </div>
+                <div class="form-group">
+                    <label class="form-label">目标字数</label>
+                    <input type="number" name="length" class="form-control" placeholder="预计字数" min="100" max="10000" value="1000">
+                </div>
+                <div class="form-group">
+                    <label class="form-label">写作风格</label>
+                    <select name="style" class="form-select">
+                        <option value="academic">学术风格</option>
+                        <option value="formal">正式风格</option>
+                        <option value="concise">简洁风格</option>
+                    </select>
+                </div>
+                <input type="hidden" name="title" value="学术写作任务">
+                <input type="hidden" name="task_type" value="writing">
+                <input type="hidden" name="priority" value="medium">
+            </form>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="btn btn-outline" data-action="close-modal">取消</button>
+            <button type="submit" form="ajax-form" class="btn btn-primary">开始写作</button>
+        </div>
+    </div>
+</div>
+
+<style>
+.dashboard {
+    max-width: 1200px;
+    margin: 0 auto;
+    padding: 20px;
+}
+
+.dashboard-header {
+    text-align: center;
+    margin-bottom: 40px;
+}
+
+.dashboard-header h1 {
+    font-size: 2.5rem;
+    color: var(--primary-color);
+    margin-bottom: 10px;
+}
+
+.dashboard-header p {
+    font-size: 1.1rem;
+    color: var(--text-muted);
+}
+
+.stats-section,
+.quick-actions,
+.recent-tasks,
+.system-status {
+    margin-bottom: 40px;
+}
+
+.stats-section h2,
+.quick-actions h2,
+.recent-tasks h2,
+.system-status h2 {
+    font-size: 1.5rem;
+    margin-bottom: 20px;
+    color: var(--dark-color);
+}
+
+.stats-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+    gap: 20px;
+}
+
+.stat-card {
+    background: white;
+    padding: 30px;
+    border-radius: 10px;
+    box-shadow: var(--shadow-md);
+    text-align: center;
+    transition: transform 0.2s;
+}
+
+.stat-card:hover {
+    transform: translateY(-5px);
+}
+
+.stat-card h3 {
+    font-size: 2.5rem;
+    font-weight: 700;
+    color: var(--primary-color);
+    margin-bottom: 10px;
+}
+
+.stat-card p {
+    color: var(--text-muted);
+    font-size: 0.9rem;
+}
+
+.action-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+    gap: 20px;
+}
+
+.action-card {
+    background: white;
+    padding: 30px;
+    border-radius: 10px;
+    box-shadow: var(--shadow-md);
+    text-align: center;
+    cursor: pointer;
+    transition: all 0.2s;
+}
+
+.action-card:hover {
+    transform: translateY(-5px);
+    box-shadow: var(--shadow-lg);
+}
+
+.action-icon {
+    font-size: 3rem;
+    margin-bottom: 15px;
+}
+
+.action-card h3 {
+    font-size: 1.2rem;
+    margin-bottom: 10px;
+    color: var(--dark-color);
+}
+
+.action-card p {
+    color: var(--text-muted);
+    font-size: 0.9rem;
+}
+
+.status-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+    gap: 20px;
+}
+
+.status-item {
+    background: white;
+    padding: 20px;
+    border-radius: 10px;
+    box-shadow: var(--shadow-md);
+    display: flex;
+    align-items: center;
+    gap: 10px;
+}
+
+.status-indicator {
+    width: 12px;
+    height: 12px;
+    border-radius: 50%;
+}
+
+.status-online {
+    background-color: var(--success-color);
+}
+
+.status-warning {
+    background-color: var(--warning-color);
+}
+
+.status-offline {
+    background-color: var(--danger-color);
+}
+
+.status-text {
+    margin-left: auto;
+    font-weight: 500;
+}
+
+.year-range {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+}
+
+.year-range input {
+    flex: 1;
+}
+
+.modal-large {
+    max-width: 800px;
+}
+
+.paper-selector {
+    max-height: 200px;
+    overflow-y: auto;
+    border: 1px solid var(--border-color);
+    border-radius: 0.375rem;
+    padding: 10px;
+}
+
+.view-all {
+    text-align: center;
+    margin-top: 20px;
+}
+
+@media (max-width: 768px) {
+    .dashboard {
+        padding: 10px;
+    }
+    
+    .dashboard-header h1 {
+        font-size: 2rem;
+    }
+    
+    .stats-grid,
+    .action-grid,
+    .status-grid {
+        grid-template-columns: 1fr;
+    }
+    
+    .year-range {
+        flex-direction: column;
+    }
+    
+    .year-range span {
+        display: none;
+    }
+}
+</style>

+ 456 - 0
Co-creation-projects/Apricity-InnocoreAI/frontend/templates/login.html

@@ -0,0 +1,456 @@
+<!-- 登录模板 -->
+<div class="login-container">
+    <div class="login-card">
+        <div class="login-header">
+            <div class="logo">
+                <h1>研创·智核</h1>
+                <p>InnoCore AI</p>
+            </div>
+            <h2>用户登录</h2>
+            <p>登录您的智能科研助手账户</p>
+        </div>
+
+        <form class="login-form ajax-form" data-endpoint="/auth/login" data-method="POST" data-redirect="/">
+            <div class="form-group">
+                <label class="form-label" for="email">邮箱地址</label>
+                <input type="email" id="email" name="email" class="form-control" placeholder="请输入邮箱地址" required>
+            </div>
+
+            <div class="form-group">
+                <label class="form-label" for="password">密码</label>
+                <input type="password" id="password" name="password" class="form-control" placeholder="请输入密码" required>
+                <div class="password-toggle" data-action="toggle-password">
+                    <span class="eye-icon">👁️</span>
+                </div>
+            </div>
+
+            <div class="form-options">
+                <label class="checkbox-label">
+                    <input type="checkbox" name="remember">
+                    <span class="checkmark"></span>
+                    记住我
+                </label>
+                <a href="#" class="forgot-password">忘记密码?</a>
+            </div>
+
+            <button type="submit" class="btn btn-primary btn-full">
+                <span class="btn-text">登录</span>
+                <span class="btn-loading" style="display: none;">
+                    <span class="spinner"></span>
+                    登录中...
+                </span>
+            </button>
+        </form>
+
+        <div class="login-footer">
+            <p>还没有账户? <a href="#" data-action="show-register">立即注册</a></p>
+        </div>
+
+        <div class="demo-info">
+            <h3>演示账户</h3>
+            <div class="demo-accounts">
+                <div class="demo-account">
+                    <strong>普通用户:</strong>
+                    <span>demo@example.com / demo123</span>
+                </div>
+                <div class="demo-account">
+                    <strong>高级用户:</strong>
+                    <span>premium@example.com / premium123</span>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="login-features">
+        <h3>平台特色</h3>
+        <div class="feature-list">
+            <div class="feature-item">
+                <div class="feature-icon">🤖</div>
+                <div class="feature-content">
+                    <h4>智能多智能体</h4>
+                    <p>猎手、矿工、教练、验证器四大智能体协同工作</p>
+                </div>
+            </div>
+            <div class="feature-item">
+                <div class="feature-icon">🔍</div>
+                <div class="feature-content">
+                    <h4>智能文献搜索</h4>
+                    <p>基于语义理解的精准文献检索</p>
+                </div>
+            </div>
+            <div class="feature-item">
+                <div class="feature-icon">📊</div>
+                <div class="feature-content">
+                    <h4>深度论文分析</h4>
+                    <p>多维度分析论文内容,提取关键信息</p>
+                </div>
+            </div>
+            <div class="feature-item">
+                <div class="feature-icon">✍️</div>
+                <div class="feature-content">
+                    <h4>学术写作助手</h4>
+                    <p>AI辅助生成高质量的学术文档</p>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- 注册模态框 -->
+<div id="register-modal" class="modal">
+    <div class="modal-content">
+        <div class="modal-header">
+            <h3 class="modal-title">用户注册</h3>
+            <button class="modal-close" data-action="close-modal">&times;</button>
+        </div>
+        <div class="modal-body">
+            <form class="register-form ajax-form" data-endpoint="/auth/register" data-method="POST" data-redirect="/">
+                <div class="form-group">
+                    <label class="form-label" for="reg-username">用户名</label>
+                    <input type="text" id="reg-username" name="username" class="form-control" placeholder="请输入用户名" required>
+                </div>
+
+                <div class="form-group">
+                    <label class="form-label" for="reg-email">邮箱地址</label>
+                    <input type="email" id="reg-email" name="email" class="form-control" placeholder="请输入邮箱地址" required>
+                </div>
+
+                <div class="form-group">
+                    <label class="form-label" for="reg-password">密码</label>
+                    <input type="password" id="reg-password" name="password" class="form-control" placeholder="请输入密码(至少6位)" required>
+                </div>
+
+                <div class="form-group">
+                    <label class="form-label" for="reg-confirm-password">确认密码</label>
+                    <input type="password" id="reg-confirm-password" name="confirm_password" class="form-control" placeholder="请再次输入密码" required>
+                </div>
+
+                <div class="form-group">
+                    <label class="form-label" for="reg-fullname">姓名(可选)</label>
+                    <input type="text" id="reg-fullname" name="full_name" class="form-control" placeholder="请输入您的姓名">
+                </div>
+
+                <div class="form-group">
+                    <label class="form-label" for="reg-institution">机构(可选)</label>
+                    <input type="text" id="reg-institution" name="institution" class="form-control" placeholder="请输入您的机构名称">
+                </div>
+
+                <div class="form-group">
+                    <label class="form-label" for="reg-field">研究领域(可选)</label>
+                    <select id="reg-field" name="research_field" class="form-select">
+                        <option value="">请选择研究领域</option>
+                        <option value="computer-science">计算机科学</option>
+                        <option value="artificial-intelligence">人工智能</option>
+                        <option value="machine-learning">机器学习</option>
+                        <option value="data-science">数据科学</option>
+                        <option value="biology">生物学</option>
+                        <option value="medicine">医学</option>
+                        <option value="physics">物理学</option>
+                        <option value="chemistry">化学</option>
+                        <option value="mathematics">数学</option>
+                        <option value="engineering">工程学</option>
+                        <option value="other">其他</option>
+                    </select>
+                </div>
+
+                <div class="form-group">
+                    <label class="checkbox-label">
+                        <input type="checkbox" name="agree_terms" required>
+                        <span class="checkmark"></span>
+                        我同意 <a href="#" class="terms-link">服务条款</a> 和 <a href="#" class="privacy-link">隐私政策</a>
+                    </label>
+                </div>
+            </form>
+        </div>
+        <div class="modal-footer">
+            <button type="button" class="btn btn-outline" data-action="close-modal">取消</button>
+            <button type="submit" form="register-form" class="btn btn-primary">注册</button>
+        </div>
+    </div>
+</div>
+
+<style>
+.login-container {
+    display: flex;
+    min-height: 100vh;
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+}
+
+.login-card {
+    flex: 1;
+    max-width: 500px;
+    margin: auto;
+    background: white;
+    padding: 40px;
+    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+}
+
+.login-header {
+    text-align: center;
+    margin-bottom: 30px;
+}
+
+.logo h1 {
+    font-size: 2rem;
+    color: var(--primary-color);
+    margin-bottom: 5px;
+}
+
+.logo p {
+    color: var(--text-muted);
+    font-size: 0.9rem;
+    margin-bottom: 20px;
+}
+
+.login-header h2 {
+    font-size: 1.8rem;
+    color: var(--dark-color);
+    margin-bottom: 10px;
+}
+
+.login-header p {
+    color: var(--text-muted);
+    font-size: 0.95rem;
+}
+
+.form-group {
+    position: relative;
+    margin-bottom: 20px;
+}
+
+.form-options {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 25px;
+}
+
+.checkbox-label {
+    display: flex;
+    align-items: center;
+    cursor: pointer;
+    font-size: 0.9rem;
+}
+
+.checkbox-label input[type="checkbox"] {
+    display: none;
+}
+
+.checkmark {
+    width: 18px;
+    height: 18px;
+    border: 2px solid var(--border-color);
+    border-radius: 3px;
+    margin-right: 8px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    transition: all 0.2s;
+}
+
+.checkbox-label input[type="checkbox"]:checked + .checkmark {
+    background-color: var(--primary-color);
+    border-color: var(--primary-color);
+}
+
+.checkbox-label input[type="checkbox"]:checked + .checkmark::after {
+    content: '✓';
+    color: white;
+    font-size: 12px;
+}
+
+.forgot-password {
+    color: var(--primary-color);
+    text-decoration: none;
+    font-size: 0.9rem;
+}
+
+.forgot-password:hover {
+    text-decoration: underline;
+}
+
+.btn-full {
+    width: 100%;
+    padding: 12px;
+    font-size: 1rem;
+    margin-bottom: 20px;
+}
+
+.login-footer {
+    text-align: center;
+    margin-bottom: 30px;
+}
+
+.login-footer p {
+    color: var(--text-muted);
+    font-size: 0.9rem;
+}
+
+.login-footer a {
+    color: var(--primary-color);
+    text-decoration: none;
+}
+
+.login-footer a:hover {
+    text-decoration: underline;
+}
+
+.demo-info {
+    background: var(--light-color);
+    padding: 20px;
+    border-radius: 8px;
+    border-left: 4px solid var(--primary-color);
+}
+
+.demo-info h3 {
+    font-size: 1rem;
+    color: var(--dark-color);
+    margin-bottom: 15px;
+}
+
+.demo-accounts {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+}
+
+.demo-account {
+    font-size: 0.85rem;
+    color: var(--text-muted);
+}
+
+.demo-account strong {
+    color: var(--dark-color);
+}
+
+.demo-account span {
+    font-family: monospace;
+    background: white;
+    padding: 2px 6px;
+    border-radius: 3px;
+    border: 1px solid var(--border-color);
+}
+
+.login-features {
+    flex: 1;
+    padding: 60px 40px;
+    color: white;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+}
+
+.login-features h3 {
+    font-size: 1.8rem;
+    margin-bottom: 30px;
+    text-align: center;
+}
+
+.feature-list {
+    display: flex;
+    flex-direction: column;
+    gap: 30px;
+}
+
+.feature-item {
+    display: flex;
+    align-items: flex-start;
+    gap: 20px;
+}
+
+.feature-icon {
+    font-size: 2.5rem;
+    flex-shrink: 0;
+}
+
+.feature-content h4 {
+    font-size: 1.2rem;
+    margin-bottom: 8px;
+}
+
+.feature-content p {
+    font-size: 0.95rem;
+    opacity: 0.9;
+    line-height: 1.5;
+}
+
+.password-toggle {
+    position: absolute;
+    right: 12px;
+    top: 50%;
+    transform: translateY(-50%);
+    cursor: pointer;
+    color: var(--text-muted);
+    transition: color 0.2s;
+}
+
+.password-toggle:hover {
+    color: var(--primary-color);
+}
+
+.eye-icon {
+    font-size: 1.2rem;
+}
+
+.terms-link,
+.privacy-link {
+    color: var(--primary-color);
+    text-decoration: none;
+}
+
+.terms-link:hover,
+.privacy-link:hover {
+    text-decoration: underline;
+}
+
+@media (max-width: 768px) {
+    .login-container {
+        flex-direction: column;
+    }
+    
+    .login-card {
+        max-width: 100%;
+        padding: 30px 20px;
+    }
+    
+    .login-features {
+        padding: 40px 20px;
+    }
+    
+    .feature-icon {
+        font-size: 2rem;
+    }
+    
+    .feature-content h4 {
+        font-size: 1.1rem;
+    }
+    
+    .demo-accounts {
+        font-size: 0.8rem;
+    }
+}
+
+@media (max-width: 480px) {
+    .login-header h2 {
+        font-size: 1.5rem;
+    }
+    
+    .form-options {
+        flex-direction: column;
+        gap: 15px;
+        align-items: flex-start;
+    }
+    
+    .demo-info {
+        padding: 15px;
+    }
+    
+    .demo-account {
+        flex-direction: column;
+        gap: 5px;
+    }
+}
+</style>

+ 83 - 0
Co-creation-projects/Apricity-InnocoreAI/install.py

@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+"""
+InnoCore AI - Installation Script
+研创·智核 - 安装脚本
+"""
+
+import subprocess
+import sys
+from pathlib import Path
+
+def install_core_deps():
+    """Install only core dependencies"""
+    print("Installing core dependencies...")
+    
+    core_deps = [
+        "fastapi==0.104.1",
+        "uvicorn[standard]==0.24.0",
+        "python-multipart==0.0.6",
+        "python-dotenv==1.0.0",
+        "pydantic==2.5.0",
+        "httpx==0.25.2",
+        "requests==2.31.0"
+    ]
+    
+    for dep in core_deps:
+        try:
+            print(f"  Installing {dep}...")
+            subprocess.check_call([sys.executable, "-m", "pip", "install", dep])
+            print(f"  [OK] {dep} installed")
+        except subprocess.CalledProcessError as e:
+            print(f"  [ERROR] Failed to install {dep}: {e}")
+            return False
+    
+    print("[OK] Core dependencies installed successfully")
+    return True
+
+def create_env_file():
+    """Create .env file"""
+    env_file = Path(".env")
+    if not env_file.exists():
+        print("Creating .env file...")
+        env_content = """# InnoCore AI Configuration
+OPENAI_API_KEY=your_openai_api_key_here
+DATABASE_URL=sqlite:///./innocore.db
+SECRET_KEY=your_secret_key_here_change_this_in_production
+DEBUG=True
+"""
+        env_file.write_text(env_content)
+        print("[OK] .env file created")
+        print("[WARNING] Please edit .env file and add your OpenAI API key")
+    else:
+        print("[OK] .env file already exists")
+
+def create_directories():
+    """Create necessary directories"""
+    dirs = ["data", "logs"]
+    for dir_path in dirs:
+        Path(dir_path).mkdir(exist_ok=True)
+    print("[OK] Directories created")
+
+def main():
+    print("InnoCore AI - Installation")
+    print("=" * 40)
+    
+    # Install core dependencies
+    if not install_core_deps():
+        print("[ERROR] Installation failed")
+        return
+    
+    # Create environment file
+    create_env_file()
+    
+    # Create directories
+    create_directories()
+    
+    print("\n[SUCCESS] Installation completed!")
+    print("Next steps:")
+    print("1. Edit .env file and add your OpenAI API key")
+    print("2. Run: python run.py")
+    print("3. Open: http://localhost:8000")
+
+if __name__ == "__main__":
+    main()

+ 218 - 0
Co-creation-projects/Apricity-InnocoreAI/main.py

@@ -0,0 +1,218 @@
+"""
+研创·智核 - 主应用入口
+"""
+
+import asyncio
+import logging
+from contextlib import asynccontextmanager
+from fastapi import FastAPI, Request
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.middleware.trustedhost import TrustedHostMiddleware
+from fastapi.responses import JSONResponse
+from fastapi.staticfiles import StaticFiles
+import uvicorn
+
+from innocore_ai.core.config import settings
+from innocore_ai.core.database import engine, Base
+from innocore_ai.core.exceptions import InnoCoreException
+from innocore_ai.api.routes import papers, users, tasks, analysis, writing
+from innocore_ai.agents.controller import AgentController
+
+# 配置日志
+logging.basicConfig(
+    level=getattr(logging, settings.LOG_LEVEL),
+    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+)
+logger = logging.getLogger(__name__)
+
+# 全局智能体控制器
+agent_controller = None
+
+@asynccontextmanager
+async def lifespan(app: FastAPI):
+    """应用生命周期管理"""
+    # 启动时执行
+    logger.info("Starting InnoCore AI application...")
+    
+    # 创建数据库表
+    Base.metadata.create_all(bind=engine)
+    logger.info("Database tables created")
+    
+    # 初始化智能体控制器
+    global agent_controller
+    agent_controller = AgentController()
+    await agent_controller.initialize()
+    logger.info("Agent controller initialized")
+    
+    yield
+    
+    # 关闭时执行
+    logger.info("Shutting down InnoCore AI application...")
+    if agent_controller:
+        await agent_controller.shutdown()
+    logger.info("Application shutdown complete")
+
+# 创建FastAPI应用
+app = FastAPI(
+    title="研创·智核 API",
+    description="基于多智能体架构的智能科研助手平台",
+    version="1.0.0",
+    docs_url="/docs" if settings.DEBUG else None,
+    redoc_url="/redoc" if settings.DEBUG else None,
+    lifespan=lifespan
+)
+
+# 添加中间件
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=settings.ALLOWED_HOSTS,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+app.add_middleware(
+    TrustedHostMiddleware,
+    allowed_hosts=settings.ALLOWED_HOSTS
+)
+
+# 挂载静态文件
+try:
+    app.mount("/static", StaticFiles(directory="innocore_ai/frontend/static"), name="static")
+except Exception:
+    # 如果路径不存在,尝试相对路径
+    app.mount("/static", StaticFiles(directory="frontend/static"), name="static")
+
+# 注册路由
+app.include_router(users.router, prefix="/api/v1/auth", tags=["认证"])
+app.include_router(papers.router, prefix="/api/v1/papers", tags=["论文管理"])
+app.include_router(tasks.router, prefix="/api/v1/tasks", tags=["任务管理"])
+app.include_router(analysis.router, prefix="/api/v1/analysis", tags=["分析报告"])
+app.include_router(writing.router, prefix="/api/v1/writing", tags=["学术写作"])
+
+# 前端路由
+@app.get("/")
+async def read_root():
+    """前端主页"""
+    try:
+        from fastapi.responses import FileResponse
+        return FileResponse("innocore_ai/frontend/index.html")
+    except Exception:
+        return FileResponse("frontend/index.html")
+
+@app.get("/dashboard")
+async def dashboard():
+    """仪表板页面"""
+    try:
+        from fastapi.responses import FileResponse
+        return FileResponse("innocore_ai/frontend/templates/dashboard.html")
+    except Exception:
+        return FileResponse("frontend/templates/dashboard.html")
+
+@app.get("/login")
+async def login():
+    """登录页面"""
+    try:
+        from fastapi.responses import FileResponse
+        return FileResponse("innocore_ai/frontend/templates/login.html")
+    except Exception:
+        return FileResponse("frontend/templates/login.html")
+
+# 处理前端路由的通配符(用于SPA)
+@app.get("/frontend/{path:path}")
+async def frontend_files(path: str):
+    """前端静态文件"""
+    try:
+        from fastapi.responses import FileResponse
+        file_path = f"innocore_ai/frontend/{path}"
+        return FileResponse(file_path)
+    except Exception:
+        file_path = f"frontend/{path}"
+        return FileResponse(file_path)
+
+@app.get("/health")
+async def health_check():
+    """健康检查"""
+    return {
+        "status": "healthy",
+        "version": "1.0.0",
+        "service": "InnoCore AI"
+    }
+
+@app.get("/api/v1/dashboard/stats")
+async def get_dashboard_stats(request: Request):
+    """获取仪表板统计数据"""
+    # 这里应该从数据库获取真实数据
+    return {
+        "total_papers": 156,
+        "total_tasks": 42,
+        "total_analyses": 28,
+        "total_writings": 15,
+        "recent_activities": [
+            {"type": "paper_added", "title": "深度学习在医学图像分析中的应用", "time": "2小时前"},
+            {"type": "task_completed", "title": "文献搜索:机器学习", "time": "4小时前"},
+            {"type": "analysis_generated", "title": "10篇论文综合分析", "time": "1天前"}
+        ]
+    }
+
+# 全局异常处理
+@app.exception_handler(InnoCoreException)
+async def innocore_exception_handler(request: Request, exc: InnoCoreException):
+    """处理自定义异常"""
+    return JSONResponse(
+        status_code=exc.status_code,
+        content={
+            "error": exc.error_code,
+            "message": exc.message,
+            "details": exc.details
+        }
+    )
+
+@app.exception_handler(Exception)
+async def general_exception_handler(request: Request, exc: Exception):
+    """处理通用异常"""
+    logger.error(f"Unhandled exception: {exc}", exc_info=True)
+    return JSONResponse(
+        status_code=500,
+        content={
+            "error": "INTERNAL_SERVER_ERROR",
+            "message": "服务器内部错误",
+            "details": str(exc) if settings.DEBUG else None
+        }
+    )
+
+# 请求日志中间件
+@app.middleware("http")
+async def log_requests(request: Request, call_next):
+    """记录请求日志"""
+    start_time = asyncio.get_event_loop().time()
+    
+    # 记录请求
+    logger.info(f"Request: {request.method} {request.url}")
+    
+    # 处理请求
+    response = await call_next(request)
+    
+    # 计算处理时间
+    process_time = asyncio.get_event_loop().time() - start_time
+    
+    # 记录响应
+    logger.info(f"Response: {response.status_code} - {process_time:.4f}s")
+    
+    # 添加处理时间到响应头
+    response.headers["X-Process-Time"] = str(process_time)
+    
+    return response
+
+def create_app():
+    """创建应用实例"""
+    return app
+
+if __name__ == "__main__":
+    uvicorn.run(
+        "innocore_ai.main:app",
+        host=settings.HOST,
+        port=settings.PORT,
+        reload=settings.DEBUG,
+        log_level=settings.LOG_LEVEL.lower()
+    )

+ 11 - 0
Co-creation-projects/Apricity-InnocoreAI/models/__init__.py

@@ -0,0 +1,11 @@
+"""
+数据模型定义
+"""
+
+from .user import User
+from .paper import Paper
+from .task import Task
+from .analysis import Analysis
+from .writing import Writing
+
+__all__ = ['User', 'Paper', 'Task', 'Analysis', 'Writing']

+ 108 - 0
Co-creation-projects/Apricity-InnocoreAI/models/analysis.py

@@ -0,0 +1,108 @@
+"""
+分析模型
+"""
+
+from datetime import datetime
+from typing import Optional, List, Dict, Any
+from pydantic import BaseModel, Field
+from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, Float, JSON
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+class AnalysisDB(Base):
+    """分析数据库模型"""
+    __tablename__ = "analysis"
+    
+    id = Column(Integer, primary_key=True, index=True)
+    title = Column(String(200), nullable=False)
+    analysis_type = Column(String(50), nullable=False)
+    paper_ids = Column(JSON)  # 分析的论文ID列表
+    methodology = Column(Text)
+    findings = Column(JSON)  # 分析发现
+    insights = Column(Text)
+    limitations = Column(Text)
+    recommendations = Column(Text)
+    confidence_score = Column(Float, default=0.0)
+    novelty_score = Column(Float, default=0.0)
+    impact_score = Column(Float, default=0.0)
+    metadata = Column(JSON)
+    user_id = Column(Integer, index=True)
+    task_id = Column(Integer, index=True)
+    created_at = Column(DateTime, default=datetime.utcnow)
+    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
+
+class Analysis(BaseModel):
+    """分析响应模型"""
+    id: int
+    title: str
+    analysis_type: str
+    methodology: Optional[str] = None
+    findings: Dict[str, Any] = {}
+    insights: Optional[str] = None
+    limitations: Optional[str] = None
+    recommendations: Optional[str] = None
+    confidence_score: float = 0.0
+    novelty_score: float = 0.0
+    impact_score: float = 0.0
+    created_at: datetime
+    
+    class Config:
+        from_attributes = True
+
+class AnalysisCreate(BaseModel):
+    """分析创建模型"""
+    title: str = Field(..., min_length=1, max_length=200)
+    analysis_type: str = Field(..., regex=r'^(comprehensive|methodology|findings|gap|trend)$')
+    paper_ids: List[int] = []
+    methodology: Optional[str] = None
+
+class AnalysisUpdate(BaseModel):
+    """分析更新模型"""
+    title: Optional[str] = None
+    methodology: Optional[str] = None
+    findings: Optional[Dict[str, Any]] = None
+    insights: Optional[str] = None
+    limitations: Optional[str] = None
+    recommendations: Optional[str] = None
+    confidence_score: Optional[float] = Field(None, ge=0.0, le=1.0)
+    novelty_score: Optional[float] = Field(None, ge=0.0, le=1.0)
+    impact_score: Optional[float] = Field(None, ge=0.0, le=1.0)
+
+class ComprehensiveAnalysis(BaseModel):
+    """综合分析结果"""
+    summary: str
+    key_findings: List[str]
+    methodological_trends: List[str]
+    research_gaps: List[str]
+    future_directions: List[str]
+    quality_assessment: Dict[str, float]
+    citation_network: Dict[str, Any]
+
+class MethodologyAnalysis(BaseModel):
+    """方法论分析结果"""
+    common_methods: List[str]
+    method_comparison: Dict[str, Any]
+    strengths_weaknesses: Dict[str, List[str]]
+    best_practices: List[str]
+
+class FindingsAnalysis(BaseModel):
+    """研究发现分析"""
+    consensus_points: List[str]
+    controversial_points: List[str]
+    emerging_patterns: List[str]
+    evidence_strength: Dict[str, float]
+
+class GapAnalysis(BaseModel):
+    """研究缺口分析"""
+    identified_gaps: List[str]
+    gap_categories: Dict[str, List[str]]
+    opportunity_areas: List[str]
+    research_questions: List[str]
+
+class TrendAnalysis(BaseModel):
+    """趋势分析结果"""
+    temporal_trends: Dict[str, Any]
+    topic_evolution: List[str]
+    emerging_topics: List[str]
+    citation_trends: Dict[str, Any]

+ 99 - 0
Co-creation-projects/Apricity-InnocoreAI/models/paper.py

@@ -0,0 +1,99 @@
+"""
+论文模型
+"""
+
+from datetime import datetime
+from typing import Optional, List, Dict, Any
+from pydantic import BaseModel, Field
+from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, Float, JSON
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+class PaperDB(Base):
+    """论文数据库模型"""
+    __tablename__ = "papers"
+    
+    id = Column(Integer, primary_key=True, index=True)
+    title = Column(String(500), nullable=False, index=True)
+    authors = Column(Text)  # JSON格式存储作者列表
+    abstract = Column(Text)
+    keywords = Column(Text)  # JSON格式存储关键词
+    publication_year = Column(Integer)
+    journal = Column(String(200))
+    doi = Column(String(100), unique=True, index=True)
+    arxiv_id = Column(String(50), unique=True, index=True)
+    pdf_url = Column(String(500))
+    pdf_path = Column(String(500))
+    full_text = Column(Text)
+    embeddings = Column(JSON)  # 存储向量嵌入
+    metadata = Column(JSON)  # 存储额外的元数据
+    quality_score = Column(Float, default=0.0)
+    relevance_score = Column(Float, default=0.0)
+    is_processed = Column(Boolean, default=False)
+    user_id = Column(Integer, index=True)
+    created_at = Column(DateTime, default=datetime.utcnow)
+    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
+
+class Paper(BaseModel):
+    """论文响应模型"""
+    id: int
+    title: str
+    authors: List[str]
+    abstract: Optional[str] = None
+    keywords: List[str] = []
+    publication_year: Optional[int] = None
+    journal: Optional[str] = None
+    doi: Optional[str] = None
+    arxiv_id: Optional[str] = None
+    pdf_url: Optional[str] = None
+    quality_score: float = 0.0
+    relevance_score: float = 0.0
+    is_processed: bool = False
+    created_at: datetime
+    
+    class Config:
+        from_attributes = True
+
+class PaperCreate(BaseModel):
+    """论文创建模型"""
+    title: str = Field(..., min_length=1, max_length=500)
+    authors: List[str] = []
+    abstract: Optional[str] = None
+    keywords: List[str] = []
+    publication_year: Optional[int] = None
+    journal: Optional[str] = None
+    doi: Optional[str] = None
+    arxiv_id: Optional[str] = None
+    pdf_url: Optional[str] = None
+
+class PaperUpdate(BaseModel):
+    """论文更新模型"""
+    title: Optional[str] = None
+    authors: Optional[List[str]] = None
+    abstract: Optional[str] = None
+    keywords: Optional[List[str]] = None
+    publication_year: Optional[int] = None
+    journal: Optional[str] = None
+    quality_score: Optional[float] = None
+    relevance_score: Optional[float] = None
+
+class PaperSearch(BaseModel):
+    """论文搜索模型"""
+    query: str = Field(..., min_length=1)
+    filters: Dict[str, Any] = {}
+    sort_by: str = "relevance"
+    limit: int = Field(default=20, ge=1, le=100)
+    offset: int = Field(default=0, ge=0)
+
+class PaperAnalysis(BaseModel):
+    """论文分析结果"""
+    paper_id: int
+    summary: str
+    key_findings: List[str]
+    methodology: str
+    limitations: List[str]
+    future_work: List[str]
+    novelty_score: float
+    impact_score: float
+    confidence_score: float

+ 88 - 0
Co-creation-projects/Apricity-InnocoreAI/models/task.py

@@ -0,0 +1,88 @@
+"""
+任务模型
+"""
+
+from datetime import datetime
+from typing import Optional, Dict, Any, List
+from pydantic import BaseModel, Field
+from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, JSON
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+class TaskDB(Base):
+    """任务数据库模型"""
+    __tablename__ = "tasks"
+    
+    id = Column(Integer, primary_key=True, index=True)
+    title = Column(String(200), nullable=False)
+    description = Column(Text)
+    task_type = Column(String(50), nullable=False)  # literature_search, analysis, writing
+    status = Column(String(20), default="pending")  # pending, running, completed, failed
+    priority = Column(String(10), default="medium")  # low, medium, high
+    parameters = Column(JSON)  # 任务参数
+    results = Column(JSON)  # 任务结果
+    error_message = Column(Text)
+    progress = Column(Integer, default=0)  # 0-100
+    user_id = Column(Integer, index=True)
+    created_at = Column(DateTime, default=datetime.utcnow)
+    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
+    completed_at = Column(DateTime)
+
+class Task(BaseModel):
+    """任务响应模型"""
+    id: int
+    title: str
+    description: Optional[str] = None
+    task_type: str
+    status: str
+    priority: str
+    progress: int = 0
+    results: Optional[Dict[str, Any]] = None
+    error_message: Optional[str] = None
+    created_at: datetime
+    updated_at: datetime
+    completed_at: Optional[datetime] = None
+    
+    class Config:
+        from_attributes = True
+
+class TaskCreate(BaseModel):
+    """任务创建模型"""
+    title: str = Field(..., min_length=1, max_length=200)
+    description: Optional[str] = None
+    task_type: str = Field(..., regex=r'^(literature_search|analysis|writing)$')
+    priority: str = Field(default="medium", regex=r'^(low|medium|high)$')
+    parameters: Dict[str, Any] = {}
+
+class TaskUpdate(BaseModel):
+    """任务更新模型"""
+    title: Optional[str] = None
+    description: Optional[str] = None
+    status: Optional[str] = None
+    priority: Optional[str] = None
+    progress: Optional[int] = Field(None, ge=0, le=100)
+    results: Optional[Dict[str, Any]] = None
+    error_message: Optional[str] = None
+
+class LiteratureSearchTask(BaseModel):
+    """文献搜索任务参数"""
+    query: str
+    max_papers: int = 20
+    year_range: Optional[tuple] = None
+    venues: List[str] = []
+    quality_threshold: float = 0.5
+
+class AnalysisTask(BaseModel):
+    """分析任务参数"""
+    paper_ids: List[int]
+    analysis_type: str = "comprehensive"  # comprehensive, methodology, findings
+    focus_areas: List[str] = []
+
+class WritingTask(BaseModel):
+    """写作任务参数"""
+    paper_ids: List[int]
+    writing_type: str = "review"  # review, summary, critique
+    outline: Optional[List[str]] = None
+    style: str = "academic"
+    length: int = 1000

+ 67 - 0
Co-creation-projects/Apricity-InnocoreAI/models/user.py

@@ -0,0 +1,67 @@
+"""
+用户模型
+"""
+
+from datetime import datetime
+from typing import Optional, List
+from pydantic import BaseModel, Field
+from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+class UserDB(Base):
+    """用户数据库模型"""
+    __tablename__ = "users"
+    
+    id = Column(Integer, primary_key=True, index=True)
+    username = Column(String(50), unique=True, index=True, nullable=False)
+    email = Column(String(100), unique=True, index=True, nullable=False)
+    hashed_password = Column(String(255), nullable=False)
+    full_name = Column(String(100))
+    institution = Column(String(200))
+    research_field = Column(String(100))
+    preferences = Column(Text)  # JSON格式存储用户偏好
+    is_active = Column(Boolean, default=True)
+    is_premium = Column(Boolean, default=False)
+    created_at = Column(DateTime, default=datetime.utcnow)
+    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
+
+class User(BaseModel):
+    """用户响应模型"""
+    id: int
+    username: str
+    email: str
+    full_name: Optional[str] = None
+    institution: Optional[str] = None
+    research_field: Optional[str] = None
+    is_active: bool = True
+    is_premium: bool = False
+    created_at: datetime
+    
+    class Config:
+        from_attributes = True
+
+class UserCreate(BaseModel):
+    """用户创建模型"""
+    username: str = Field(..., min_length=3, max_length=50)
+    email: str = Field(..., regex=r'^[^@]+@[^@]+\.[^@]+$')
+    password: str = Field(..., min_length=6)
+    full_name: Optional[str] = None
+    institution: Optional[str] = None
+    research_field: Optional[str] = None
+
+class UserUpdate(BaseModel):
+    """用户更新模型"""
+    full_name: Optional[str] = None
+    institution: Optional[str] = None
+    research_field: Optional[str] = None
+    preferences: Optional[str] = None
+
+class UserPreferences(BaseModel):
+    """用户偏好设置"""
+    research_interests: List[str] = []
+    citation_style: str = "APA"
+    language: str = "zh"
+    notification_enabled: bool = True
+    auto_save: bool = True

+ 111 - 0
Co-creation-projects/Apricity-InnocoreAI/models/writing.py

@@ -0,0 +1,111 @@
+"""
+写作模型
+"""
+
+from datetime import datetime
+from typing import Optional, List, Dict, Any
+from pydantic import BaseModel, Field
+from sqlalchemy import Column, Integer, String, DateTime, Text, Boolean, Float, JSON
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+class WritingDB(Base):
+    """写作数据库模型"""
+    __tablename__ = "writing"
+    
+    id = Column(Integer, primary_key=True, index=True)
+    title = Column(String(200), nullable=False)
+    writing_type = Column(String(50), nullable=False)  # review, summary, critique, proposal
+    content = Column(Text)
+    outline = Column(JSON)  # 大纲结构
+    sections = Column(JSON)  # 章节内容
+    citations = Column(JSON)  # 引用信息
+    metadata = Column(JSON)  # 额外元数据
+    quality_score = Column(Float, default=0.0)
+    word_count = Column(Integer, default=0)
+    status = Column(String(20), default="draft")  # draft, reviewing, completed
+    paper_ids = Column(JSON)  # 参考论文ID列表
+    user_id = Column(Integer, index=True)
+    task_id = Column(Integer, index=True)
+    created_at = Column(DateTime, default=datetime.utcnow)
+    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
+
+class Writing(BaseModel):
+    """写作响应模型"""
+    id: int
+    title: str
+    writing_type: str
+    content: Optional[str] = None
+    outline: List[Dict[str, Any]] = []
+    sections: Dict[str, str] = {}
+    citations: List[Dict[str, Any]] = []
+    quality_score: float = 0.0
+    word_count: int = 0
+    status: str = "draft"
+    created_at: datetime
+    
+    class Config:
+        from_attributes = True
+
+class WritingCreate(BaseModel):
+    """写作创建模型"""
+    title: str = Field(..., min_length=1, max_length=200)
+    writing_type: str = Field(..., regex=r'^(review|summary|critique|proposal)$')
+    paper_ids: List[int] = []
+    outline: Optional[List[Dict[str, Any]]] = None
+
+class WritingUpdate(BaseModel):
+    """写作更新模型"""
+    title: Optional[str] = None
+    content: Optional[str] = None
+    outline: Optional[List[Dict[str, Any]]] = None
+    sections: Optional[Dict[str, str]] = None
+    citations: Optional[List[Dict[str, Any]]] = None
+    status: Optional[str] = None
+    quality_score: Optional[float] = Field(None, ge=0.0, le=1.0)
+
+class LiteratureReview(BaseModel):
+    """文献综述"""
+    introduction: str
+    methodology_review: str
+    findings_synthesis: str
+    discussion: str
+    conclusion: str
+    references: List[Dict[str, Any]]
+
+class PaperSummary(BaseModel):
+    """论文总结"""
+    background: str
+    methods: str
+    results: str
+    conclusions: str
+    significance: str
+
+class PaperCritique(BaseModel):
+    """论文评述"""
+    strengths: List[str]
+    weaknesses: List[str]
+    methodological_issues: List[str]
+    interpretation_concerns: List[str]
+    suggestions: List[str]
+
+class ResearchProposal(BaseModel):
+    """研究提案"""
+    background: str
+    problem_statement: str
+    research_questions: List[str]
+    methodology: str
+    expected_outcomes: str
+    significance: str
+    timeline: str
+
+class WritingSection(BaseModel):
+    """写作章节"""
+    title: str
+    content: str
+    subsections: List['WritingSection'] = []
+    citations: List[str] = []
+
+# 解决前向引用
+WritingSection.model_rebuild()

+ 82 - 0
Co-creation-projects/Apricity-InnocoreAI/requirements.txt

@@ -0,0 +1,82 @@
+# InnoCore AI - Core Dependencies
+# 核心依赖列表 - 已验证可用
+
+# Web Framework
+fastapi==0.121.3
+uvicorn[standard]==0.38.0
+python-multipart==0.0.20
+starlette==0.50.0
+
+# Database
+sqlalchemy==2.0.44
+asyncpg==0.30.0
+redis==7.1.0
+
+# AI & ML Framework
+hello-agents[all]>=0.2.7  # HelloAgent 框架(包含所有功能)
+openai>=1.0.0  # OpenAI API 客户端
+tiktoken>=0.5.0  # Token 计数工具
+
+# Vector Database
+chromadb==1.3.5
+qdrant-client==1.16.0
+
+# Deep Learning
+torch==2.9.1
+transformers==4.57.1
+sentence-transformers==5.1.2
+safetensors==0.7.0
+
+# Data Processing
+numpy==2.2.6
+scipy==1.15.3
+scikit-learn==1.7.2
+pandas==2.1.4
+
+# HTTP Client
+httpx==0.28.1
+aiohttp==3.13.2
+requests==2.32.5
+
+# Data Validation
+pydantic==2.12.4
+pydantic-core==2.41.5
+
+# Utilities
+python-dotenv==1.2.1
+pyyaml==6.0.3
+tenacity==9.1.2
+tqdm==4.67.1
+click==8.3.1
+
+# Monitoring & Telemetry
+opentelemetry-api==1.38.0
+opentelemetry-sdk==1.38.0
+opentelemetry-exporter-otlp-proto-grpc==1.38.0
+
+# Additional Dependencies
+huggingface-hub==0.36.0
+tokenizers==0.22.1
+jinja2==3.1.6
+rich==14.2.0
+typer==0.20.0
+jsonpatch==1.33
+orjson==3.11.4
+protobuf==6.33.1
+grpcio==1.76.0
+kubernetes==34.1.0
+onnxruntime==1.23.2
+bcrypt==5.0.0
+
+# Literature Search & Processing
+feedparser==6.0.12
+beautifulsoup4==4.14.2
+lxml==6.0.2
+arxiv==2.3.1
+scholarly==1.7.11
+selenium==4.38.0
+
+# PDF Processing
+PyPDF2==3.0.1
+pdfplumber==0.11.0
+pypdf==3.17.4

+ 34 - 0
Co-creation-projects/Apricity-InnocoreAI/run.py

@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+"""
+InnoCore AI - Simple Run Script
+研创·智核 - 简单运行脚本
+"""
+
+import os
+import sys
+import uvicorn
+from pathlib import Path
+
+def main():
+    """Run the full InnoCore AI application"""
+    print("Starting InnoCore AI...")
+    
+    # Add current directory to Python path
+    current_dir = Path(__file__).parent
+    sys.path.insert(0, str(current_dir))
+    
+    # Start server with the full API
+    print("Server will be available at: http://localhost:8000")
+    print("API docs at: http://localhost:8000/docs")
+    print("Health check at: http://localhost:8000/health")
+    
+    uvicorn.run(
+        "api.main:app",
+        host="0.0.0.0",
+        port=8000,
+        reload=True,
+        log_level="info"
+    )
+
+if __name__ == "__main__":
+    main()

+ 11 - 0
Co-creation-projects/Apricity-InnocoreAI/services/__init__.py

@@ -0,0 +1,11 @@
+"""
+服务层
+"""
+
+from .paper_service import PaperService
+from .task_service import TaskService
+from .analysis_service import AnalysisService
+from .writing_service import WritingService
+from .user_service import UserService
+
+__all__ = ['PaperService', 'TaskService', 'AnalysisService', 'WritingService', 'UserService']

+ 172 - 0
Co-creation-projects/Apricity-InnocoreAI/services/analysis_service.py

@@ -0,0 +1,172 @@
+"""
+分析服务
+"""
+
+from typing import Optional, List, Dict, Any
+from sqlalchemy.orm import Session
+from ..core.database import get_db
+from ..models.analysis import AnalysisDB, Analysis, AnalysisCreate, AnalysisUpdate
+from ..core.exceptions import AnalysisNotFoundError
+from ..services.paper_service import PaperService
+import json
+
+class AnalysisService:
+    """分析服务类"""
+    
+    def __init__(self, db: Session):
+        self.db = db
+        self.paper_service = PaperService(db)
+    
+    def get_analysis_by_id(self, analysis_id: int) -> Optional[Analysis]:
+        """根据ID获取分析"""
+        analysis_db = self.db.query(AnalysisDB).filter(AnalysisDB.id == analysis_id).first()
+        if not analysis_db:
+            raise AnalysisNotFoundError(f"Analysis with id {analysis_id} not found")
+        return Analysis.from_orm(analysis_db)
+    
+    def get_analyses_by_user(self, user_id: int, skip: int = 0, limit: int = 20) -> List[Analysis]:
+        """获取用户的分析列表"""
+        analyses_db = self.db.query(AnalysisDB).filter(
+            AnalysisDB.user_id == user_id
+        ).order_by(AnalysisDB.created_at.desc()).offset(skip).limit(limit).all()
+        return [Analysis.from_orm(analysis) for analysis in analyses_db]
+    
+    def create_analysis(self, analysis_create: AnalysisCreate, user_id: int, task_id: Optional[int] = None) -> Analysis:
+        """创建分析"""
+        analysis_db = AnalysisDB(
+            title=analysis_create.title,
+            analysis_type=analysis_create.analysis_type,
+            paper_ids=json.dumps(analysis_create.paper_ids),
+            methodology=analysis_create.methodology,
+            user_id=user_id,
+            task_id=task_id
+        )
+        
+        self.db.add(analysis_db)
+        self.db.commit()
+        self.db.refresh(analysis_db)
+        
+        return Analysis.from_orm(analysis_db)
+    
+    def update_analysis(self, analysis_id: int, analysis_update: AnalysisUpdate) -> Analysis:
+        """更新分析"""
+        analysis_db = self.db.query(AnalysisDB).filter(AnalysisDB.id == analysis_id).first()
+        if not analysis_db:
+            raise AnalysisNotFoundError(f"Analysis with id {analysis_id} not found")
+        
+        # 更新字段
+        update_data = analysis_update.dict(exclude_unset=True)
+        for field, value in update_data.items():
+            if field == 'findings':
+                setattr(analysis_db, field, json.dumps(value))
+            else:
+                setattr(analysis_db, field, value)
+        
+        self.db.commit()
+        self.db.refresh(analysis_db)
+        
+        return Analysis.from_orm(analysis_db)
+    
+    def delete_analysis(self, analysis_id: int) -> bool:
+        """删除分析"""
+        analysis_db = self.db.query(AnalysisDB).filter(AnalysisDB.id == analysis_id).first()
+        if not analysis_db:
+            raise AnalysisNotFoundError(f"Analysis with id {analysis_id} not found")
+        
+        self.db.delete(analysis_db)
+        self.db.commit()
+        
+        return True
+    
+    def get_analysis_statistics(self, user_id: int) -> Dict[str, Any]:
+        """获取分析统计信息"""
+        total_analyses = self.db.query(AnalysisDB).filter(AnalysisDB.user_id == user_id).count()
+        
+        # 按类型统计
+        type_stats = self.db.query(
+            AnalysisDB.analysis_type,
+            self.db.func.count(AnalysisDB.id)
+        ).filter(AnalysisDB.user_id == user_id).group_by(AnalysisDB.analysis_type).all()
+        
+        # 平均分数
+        avg_scores = self.db.query(
+            self.db.func.avg(AnalysisDB.confidence_score),
+            self.db.func.avg(AnalysisDB.novelty_score),
+            self.db.func.avg(AnalysisDB.impact_score)
+        ).filter(AnalysisDB.user_id == user_id).first()
+        
+        return {
+            'total_analyses': total_analyses,
+            'type_distribution': dict(type_stats),
+            'average_confidence': float(avg_scores[0] or 0),
+            'average_novelty': float(avg_scores[1] or 0),
+            'average_impact': float(avg_scores[2] or 0)
+        }
+    
+    def get_related_analyses(self, analysis_id: int, limit: int = 5) -> List[Analysis]:
+        """获取相关分析"""
+        analysis_db = self.db.query(AnalysisDB).filter(AnalysisDB.id == analysis_id).first()
+        if not analysis_db:
+            raise AnalysisNotFoundError(f"Analysis with id {analysis_id} not found")
+        
+        # 获取相同类型的分析
+        related_analyses = self.db.query(AnalysisDB).filter(
+            and_(
+                AnalysisDB.user_id == analysis_db.user_id,
+                AnalysisDB.analysis_type == analysis_db.analysis_type,
+                AnalysisDB.id != analysis_id
+            )
+        ).order_by(AnalysisDB.created_at.desc()).limit(limit).all()
+        
+        return [Analysis.from_orm(analysis) for analysis in related_analyses]
+    
+    def export_analysis(self, analysis_id: int, format: str = "json") -> Dict[str, Any]:
+        """导出分析结果"""
+        analysis = self.get_analysis_by_id(analysis_id)
+        
+        if format == "json":
+            return analysis.dict()
+        elif format == "markdown":
+            return self._export_to_markdown(analysis)
+        elif format == "pdf":
+            return self._export_to_pdf(analysis)
+        else:
+            raise ValueError(f"Unsupported export format: {format}")
+    
+    def _export_to_markdown(self, analysis: Analysis) -> str:
+        """导出为Markdown格式"""
+        markdown = f"# {analysis.title}\n\n"
+        markdown += f"**分析类型**: {analysis.analysis_type}\n\n"
+        markdown += f"**创建时间**: {analysis.created_at.strftime('%Y-%m-%d %H:%M:%S')}\n\n"
+        
+        if analysis.methodology:
+            markdown += f"## 方法论\n\n{analysis.methodology}\n\n"
+        
+        if analysis.findings:
+            markdown += "## 主要发现\n\n"
+            for key, value in analysis.findings.items():
+                markdown += f"### {key}\n\n{value}\n\n"
+        
+        if analysis.insights:
+            markdown += f"## 洞察\n\n{analysis.insights}\n\n"
+        
+        if analysis.limitations:
+            markdown += f"## 局限性\n\n{analysis.limitations}\n\n"
+        
+        if analysis.recommendations:
+            markdown += f"## 建议\n\n{analysis.recommendations}\n\n"
+        
+        # 添加评分
+        markdown += "## 评分\n\n"
+        markdown += f"- **置信度**: {analysis.confidence_score:.2f}\n"
+        markdown += f"- **新颖性**: {analysis.novelty_score:.2f}\n"
+        markdown += f"- **影响力**: {analysis.impact_score:.2f}\n"
+        
+        return markdown
+    
+    def _export_to_pdf(self, analysis: Analysis) -> bytes:
+        """导出为PDF格式"""
+        # 这里可以使用reportlab或其他PDF生成库
+        # 暂时返回Markdown内容的字节
+        markdown_content = self._export_to_markdown(analysis)
+        return markdown_content.encode('utf-8')

+ 248 - 0
Co-creation-projects/Apricity-InnocoreAI/services/paper_service.py

@@ -0,0 +1,248 @@
+"""
+论文服务
+"""
+
+from typing import Optional, List, Dict, Any
+from sqlalchemy.orm import Session
+from sqlalchemy import and_, or_, desc
+from ..core.database import get_db
+from ..core.vector_store import VectorStore
+from ..models.paper import PaperDB, Paper, PaperCreate, PaperUpdate, PaperSearch
+from ..core.exceptions import PaperNotFoundError, PaperAlreadyExistsError
+from ..utils.pdf_parser import PDFParser
+from ..utils.embedding import EmbeddingService
+import json
+
+class PaperService:
+    """论文服务类"""
+    
+    def __init__(self, db: Session):
+        self.db = db
+        self.vector_store = VectorStore()
+        self.pdf_parser = PDFParser()
+        self.embedding_service = EmbeddingService()
+    
+    def get_paper_by_id(self, paper_id: int) -> Optional[Paper]:
+        """根据ID获取论文"""
+        paper_db = self.db.query(PaperDB).filter(PaperDB.id == paper_id).first()
+        if not paper_db:
+            raise PaperNotFoundError(f"Paper with id {paper_id} not found")
+        return Paper.from_orm(paper_db)
+    
+    def get_papers_by_user(self, user_id: int, skip: int = 0, limit: int = 20) -> List[Paper]:
+        """获取用户的论文列表"""
+        papers_db = self.db.query(PaperDB).filter(
+            PaperDB.user_id == user_id
+        ).offset(skip).limit(limit).all()
+        return [Paper.from_orm(paper) for paper in papers_db]
+    
+    def create_paper(self, paper_create: PaperCreate, user_id: int) -> Paper:
+        """创建论文记录"""
+        # 检查DOI是否已存在
+        if paper_create.doi:
+            existing = self.db.query(PaperDB).filter(PaperDB.doi == paper_create.doi).first()
+            if existing:
+                raise PaperAlreadyExistsError(f"Paper with DOI {paper_create.doi} already exists")
+        
+        # 检查arXiv ID是否已存在
+        if paper_create.arxiv_id:
+            existing = self.db.query(PaperDB).filter(PaperDB.arxiv_id == paper_create.arxiv_id).first()
+            if existing:
+                raise PaperAlreadyExistsError(f"Paper with arXiv ID {paper_create.arxiv_id} already exists")
+        
+        # 创建论文记录
+        paper_db = PaperDB(
+            title=paper_create.title,
+            authors=json.dumps(paper_create.authors),
+            abstract=paper_create.abstract,
+            keywords=json.dumps(paper_create.keywords),
+            publication_year=paper_create.publication_year,
+            journal=paper_create.journal,
+            doi=paper_create.doi,
+            arxiv_id=paper_create.arxiv_id,
+            pdf_url=paper_create.pdf_url,
+            user_id=user_id
+        )
+        
+        self.db.add(paper_db)
+        self.db.commit()
+        self.db.refresh(paper_db)
+        
+        # 异步处理PDF和嵌入
+        self._process_paper_async(paper_db.id)
+        
+        return Paper.from_orm(paper_db)
+    
+    def update_paper(self, paper_id: int, paper_update: PaperUpdate) -> Paper:
+        """更新论文信息"""
+        paper_db = self.db.query(PaperDB).filter(PaperDB.id == paper_id).first()
+        if not paper_db:
+            raise PaperNotFoundError(f"Paper with id {paper_id} not found")
+        
+        # 更新字段
+        update_data = paper_update.dict(exclude_unset=True)
+        for field, value in update_data.items():
+            if field in ['authors', 'keywords']:
+                setattr(paper_db, field, json.dumps(value))
+            else:
+                setattr(paper_db, field, value)
+        
+        self.db.commit()
+        self.db.refresh(paper_db)
+        
+        return Paper.from_orm(paper_db)
+    
+    def delete_paper(self, paper_id: int) -> bool:
+        """删除论文"""
+        paper_db = self.db.query(PaperDB).filter(PaperDB.id == paper_id).first()
+        if not paper_db:
+            raise PaperNotFoundError(f"Paper with id {paper_id} not found")
+        
+        # 从向量存储中删除
+        if paper_db.embeddings:
+            self.vector_store.delete_document(paper_id)
+        
+        self.db.delete(paper_db)
+        self.db.commit()
+        
+        return True
+    
+    def search_papers(self, search: PaperSearch, user_id: int) -> List[Paper]:
+        """搜索论文"""
+        query = self.db.query(PaperDB).filter(PaperDB.user_id == user_id)
+        
+        # 文本搜索
+        if search.query:
+            search_filter = or_(
+                PaperDB.title.contains(search.query),
+                PaperDB.abstract.contains(search.query),
+                PaperDB.keywords.contains(search.query)
+            )
+            query = query.filter(search_filter)
+        
+        # 应用过滤器
+        filters = search.filters
+        if 'year_range' in filters:
+            start_year, end_year = filters['year_range']
+            query = query.filter(
+                and_(
+                    PaperDB.publication_year >= start_year,
+                    PaperDB.publication_year <= end_year
+                )
+            )
+        
+        if 'venues' in filters:
+            query = query.filter(PaperDB.journal.in_(filters['venues']))
+        
+        if 'authors' in filters:
+            author_filter = or_(*[
+                PaperDB.authors.contains(author) for author in filters['authors']
+            ])
+            query = query.filter(author_filter)
+        
+        # 排序
+        if search.sort_by == "relevance":
+            query = query.order_by(desc(PaperDB.relevance_score))
+        elif search.sort_by == "quality":
+            query = query.order_by(desc(PaperDB.quality_score))
+        elif search.sort_by == "year":
+            query = query.order_by(desc(PaperDB.publication_year))
+        else:
+            query = query.order_by(desc(PaperDB.created_at))
+        
+        # 分页
+        papers_db = query.offset(search.offset).limit(search.limit).all()
+        return [Paper.from_orm(paper) for paper in papers_db]
+    
+    def semantic_search(self, query: str, user_id: int, limit: int = 10) -> List[Paper]:
+        """语义搜索论文"""
+        # 生成查询向量
+        query_embedding = self.embedding_service.get_embedding(query)
+        
+        # 在向量存储中搜索
+        results = self.vector_store.search(query_embedding, user_id, limit)
+        
+        # 获取对应的论文
+        paper_ids = [result['id'] for result in results]
+        papers_db = self.db.query(PaperDB).filter(
+            and_(
+                PaperDB.id.in_(paper_ids),
+                PaperDB.user_id == user_id
+            )
+        ).all()
+        
+        # 按相似度排序
+        paper_dict = {paper.id: paper for paper in papers_db}
+        sorted_papers = []
+        for result in results:
+            if result['id'] in paper_dict:
+                paper = Paper.from_orm(paper_dict[result['id']])
+                paper.relevance_score = result['score']
+                sorted_papers.append(paper)
+        
+        return sorted_papers
+    
+    def _process_paper_async(self, paper_id: int):
+        """异步处理论文(PDF解析和嵌入生成)"""
+        try:
+            paper_db = self.db.query(PaperDB).filter(PaperDB.id == paper_id).first()
+            if not paper_db:
+                return
+            
+            # 如果有PDF URL,下载并解析
+            if paper_db.pdf_url and not paper_db.full_text:
+                full_text = self.pdf_parser.parse_pdf_from_url(paper_db.pdf_url)
+                if full_text:
+                    paper_db.full_text = full_text
+            
+            # 生成嵌入
+            text_to_embed = paper_db.title + " " + (paper_db.abstract or "")
+            if paper_db.full_text:
+                text_to_embed += " " + paper_db.full_text
+            
+            embedding = self.embedding_service.get_embedding(text_to_embed)
+            paper_db.embeddings = embedding.tolist()
+            
+            # 添加到向量存储
+            self.vector_store.add_document(
+                doc_id=paper_id,
+                embedding=embedding,
+                metadata={
+                    'title': paper_db.title,
+                    'user_id': paper_db.user_id
+                }
+            )
+            
+            paper_db.is_processed = True
+            self.db.commit()
+            
+        except Exception as e:
+            print(f"Error processing paper {paper_id}: {e}")
+            # 可以在这里添加错误日志记录
+    
+    def get_paper_statistics(self, user_id: int) -> Dict[str, Any]:
+        """获取论文统计信息"""
+        total_papers = self.db.query(PaperDB).filter(PaperDB.user_id == user_id).count()
+        processed_papers = self.db.query(PaperDB).filter(
+            and_(PaperDB.user_id == user_id, PaperDB.is_processed == True)
+        ).count()
+        
+        # 按年份统计
+        year_stats = self.db.query(
+            PaperDB.publication_year,
+            self.db.func.count(PaperDB.id)
+        ).filter(PaperDB.user_id == user_id).group_by(PaperDB.publication_year).all()
+        
+        # 按期刊统计
+        journal_stats = self.db.query(
+            PaperDB.journal,
+            self.db.func.count(PaperDB.id)
+        ).filter(PaperDB.user_id == user_id).group_by(PaperDB.journal).all()
+        
+        return {
+            'total_papers': total_papers,
+            'processed_papers': processed_papers,
+            'processing_rate': processed_papers / total_papers if total_papers > 0 else 0,
+            'year_distribution': dict(year_stats),
+            'journal_distribution': dict(journal_stats)
+        }

+ 309 - 0
Co-creation-projects/Apricity-InnocoreAI/services/task_service.py

@@ -0,0 +1,309 @@
+"""
+任务服务
+"""
+
+from typing import Optional, List, Dict, Any
+from sqlalchemy.orm import Session
+from datetime import datetime
+from ..core.database import get_db
+from ..models.task import TaskDB, Task, TaskCreate, TaskUpdate
+from ..core.exceptions import TaskNotFoundError
+from ..agents.controller import AgentController
+import json
+import asyncio
+
+class TaskService:
+    """任务服务类"""
+    
+    def __init__(self, db: Session):
+        self.db = db
+        self.agent_controller = AgentController()
+    
+    def get_task_by_id(self, task_id: int) -> Optional[Task]:
+        """根据ID获取任务"""
+        task_db = self.db.query(TaskDB).filter(TaskDB.id == task_id).first()
+        if not task_db:
+            raise TaskNotFoundError(f"Task with id {task_id} not found")
+        return Task.from_orm(task_db)
+    
+    def get_tasks_by_user(self, user_id: int, skip: int = 0, limit: int = 20) -> List[Task]:
+        """获取用户的任务列表"""
+        tasks_db = self.db.query(TaskDB).filter(
+            TaskDB.user_id == user_id
+        ).order_by(TaskDB.created_at.desc()).offset(skip).limit(limit).all()
+        return [Task.from_orm(task) for task in tasks_db]
+    
+    def create_task(self, task_create: TaskCreate, user_id: int) -> Task:
+        """创建任务"""
+        task_db = TaskDB(
+            title=task_create.title,
+            description=task_create.description,
+            task_type=task_create.task_type,
+            priority=task_create.priority,
+            parameters=task_create.parameters,
+            user_id=user_id
+        )
+        
+        self.db.add(task_db)
+        self.db.commit()
+        self.db.refresh(task_db)
+        
+        # 异步执行任务
+        self._execute_task_async(task_db.id)
+        
+        return Task.from_orm(task_db)
+    
+    def update_task(self, task_id: int, task_update: TaskUpdate) -> Task:
+        """更新任务"""
+        task_db = self.db.query(TaskDB).filter(TaskDB.id == task_id).first()
+        if not task_db:
+            raise TaskNotFoundError(f"Task with id {task_id} not found")
+        
+        # 更新字段
+        update_data = task_update.dict(exclude_unset=True)
+        for field, value in update_data.items():
+            setattr(task_db, field, value)
+        
+        # 如果任务完成,设置完成时间
+        if task_update.status == "completed":
+            task_db.completed_at = datetime.utcnow()
+        
+        self.db.commit()
+        self.db.refresh(task_db)
+        
+        return Task.from_orm(task_db)
+    
+    def delete_task(self, task_id: int) -> bool:
+        """删除任务"""
+        task_db = self.db.query(TaskDB).filter(TaskDB.id == task_id).first()
+        if not task_db:
+            raise TaskNotFoundError(f"Task with id {task_id} not found")
+        
+        self.db.delete(task_db)
+        self.db.commit()
+        
+        return True
+    
+    def cancel_task(self, task_id: int) -> Task:
+        """取消任务"""
+        return self.update_task(task_id, TaskUpdate(status="failed", error_message="Task cancelled by user"))
+    
+    def retry_task(self, task_id: int) -> Task:
+        """重试任务"""
+        # 重置任务状态
+        task = self.update_task(task_id, TaskUpdate(
+            status="pending",
+            progress=0,
+            error_message=None
+        ))
+        
+        # 重新执行任务
+        self._execute_task_async(task_id)
+        
+        return task
+    
+    def get_task_statistics(self, user_id: int) -> Dict[str, Any]:
+        """获取任务统计信息"""
+        total_tasks = self.db.query(TaskDB).filter(TaskDB.user_id == user_id).count()
+        
+        # 按状态统计
+        status_stats = self.db.query(
+            TaskDB.status,
+            self.db.func.count(TaskDB.id)
+        ).filter(TaskDB.user_id == user_id).group_by(TaskDB.status).all()
+        
+        # 按类型统计
+        type_stats = self.db.query(
+            TaskDB.task_type,
+            self.db.func.count(TaskDB.id)
+        ).filter(TaskDB.user_id == user_id).group_by(TaskDB.task_type).all()
+        
+        # 成功率
+        completed_tasks = self.db.query(TaskDB).filter(
+            and_(TaskDB.user_id == user_id, TaskDB.status == "completed")
+        ).count()
+        
+        success_rate = completed_tasks / total_tasks if total_tasks > 0 else 0
+        
+        return {
+            'total_tasks': total_tasks,
+            'success_rate': success_rate,
+            'status_distribution': dict(status_stats),
+            'type_distribution': dict(type_stats)
+        }
+    
+    def _execute_task_async(self, task_id: int):
+        """异步执行任务"""
+        try:
+            # 获取任务信息
+            task_db = self.db.query(TaskDB).filter(TaskDB.id == task_id).first()
+            if not task_db:
+                return
+            
+            # 更新任务状态为运行中
+            task_db.status = "running"
+            task_db.progress = 0
+            self.db.commit()
+            
+            # 根据任务类型执行相应的智能体
+            if task_db.task_type == "literature_search":
+                result = asyncio.run(self._execute_literature_search(task_db))
+            elif task_db.task_type == "analysis":
+                result = asyncio.run(self._execute_analysis(task_db))
+            elif task_db.task_type == "writing":
+                result = asyncio.run(self._execute_writing(task_db))
+            else:
+                raise ValueError(f"Unknown task type: {task_db.task_type}")
+            
+            # 更新任务结果
+            task_db.status = "completed"
+            task_db.progress = 100
+            task_db.results = result
+            task_db.completed_at = datetime.utcnow()
+            self.db.commit()
+            
+        except Exception as e:
+            # 更新任务状态为失败
+            task_db.status = "failed"
+            task_db.error_message = str(e)
+            self.db.commit()
+    
+    async def _execute_literature_search(self, task_db: TaskDB) -> Dict[str, Any]:
+        """执行文献搜索任务"""
+        parameters = task_db.parameters or {}
+        query = parameters.get('query', '')
+        max_papers = parameters.get('max_papers', 20)
+        
+        # 使用猎手智能体进行文献搜索
+        hunter_agent = self.agent_controller.get_agent('hunter')
+        
+        # 更新进度
+        await self._update_task_progress(task_db.id, 20)
+        
+        # 执行搜索
+        search_results = await hunter_agent.search_papers(query, max_papers)
+        
+        # 更新进度
+        await self._update_task_progress(task_db.id, 60)
+        
+        # 使用矿工智能体进行深度挖掘
+        miner_agent = self.agent_controller.get_agent('miner')
+        enriched_results = await miner_agent.enrich_papers(search_results)
+        
+        # 更新进度
+        await self._update_task_progress(task_db.id, 90)
+        
+        # 保存论文到数据库
+        paper_service = PaperService(self.db)
+        saved_papers = []
+        for paper_data in enriched_results:
+            try:
+                paper = paper_service.create_paper(
+                    PaperCreate(**paper_data),
+                    task_db.user_id
+                )
+                saved_papers.append(paper.dict())
+            except Exception as e:
+                print(f"Error saving paper: {e}")
+        
+        return {
+            'query': query,
+            'total_found': len(enriched_results),
+            'papers_saved': len(saved_papers),
+            'papers': saved_papers
+        }
+    
+    async def _execute_analysis(self, task_db: TaskDB) -> Dict[str, Any]:
+        """执行分析任务"""
+        parameters = task_db.parameters or {}
+        paper_ids = parameters.get('paper_ids', [])
+        analysis_type = parameters.get('analysis_type', 'comprehensive')
+        
+        # 使用教练智能体进行分析
+        coach_agent = self.agent_controller.get_agent('coach')
+        
+        # 更新进度
+        await self._update_task_progress(task_db.id, 30)
+        
+        # 执行分析
+        analysis_result = await coach_agent.analyze_papers(paper_ids, analysis_type)
+        
+        # 更新进度
+        await self._update_task_progress(task_db.id, 80)
+        
+        # 保存分析结果
+        analysis_service = AnalysisService(self.db)
+        analysis = analysis_service.create_analysis(
+            {
+                'title': f"Analysis of {len(paper_ids)} papers",
+                'analysis_type': analysis_type,
+                'paper_ids': paper_ids,
+                'methodology': analysis_result.get('methodology', ''),
+                'findings': analysis_result.get('findings', {}),
+                'insights': analysis_result.get('insights', ''),
+                'limitations': analysis_result.get('limitations', ''),
+                'recommendations': analysis_result.get('recommendations', ''),
+                'confidence_score': analysis_result.get('confidence_score', 0.0),
+                'novelty_score': analysis_result.get('novelty_score', 0.0),
+                'impact_score': analysis_result.get('impact_score', 0.0)
+            },
+            task_db.user_id,
+            task_db.id
+        )
+        
+        return {
+            'analysis_id': analysis.id,
+            'analysis_type': analysis_type,
+            'papers_analyzed': len(paper_ids),
+            'result': analysis.dict()
+        }
+    
+    async def _execute_writing(self, task_db: TaskDB) -> Dict[str, Any]:
+        """执行写作任务"""
+        parameters = task_db.parameters or {}
+        paper_ids = parameters.get('paper_ids', [])
+        writing_type = parameters.get('writing_type', 'review')
+        outline = parameters.get('outline')
+        
+        # 使用教练智能体进行写作
+        coach_agent = self.agent_controller.get_agent('coach')
+        
+        # 更新进度
+        await self._update_task_progress(task_db.id, 25)
+        
+        # 生成内容
+        writing_result = await coach_agent.generate_writing(paper_ids, writing_type, outline)
+        
+        # 更新进度
+        await self._update_task_progress(task_db.id, 75)
+        
+        # 保存写作结果
+        writing_service = WritingService(self.db)
+        writing = writing_service.create_writing(
+            {
+                'title': writing_result.get('title', 'Generated Writing'),
+                'writing_type': writing_type,
+                'content': writing_result.get('content', ''),
+                'outline': writing_result.get('outline', []),
+                'sections': writing_result.get('sections', {}),
+                'citations': writing_result.get('citations', []),
+                'paper_ids': paper_ids
+            },
+            task_db.user_id,
+            task_db.id
+        )
+        
+        return {
+            'writing_id': writing.id,
+            'writing_type': writing_type,
+            'papers_referenced': len(paper_ids),
+            'word_count': writing.word_count,
+            'result': writing.dict()
+        }
+    
+    async def _update_task_progress(self, task_id: int, progress: int):
+        """更新任务进度"""
+        task_db = self.db.query(TaskDB).filter(TaskDB.id == task_id).first()
+        if task_db:
+            task_db.progress = progress
+            self.db.commit()

+ 127 - 0
Co-creation-projects/Apricity-InnocoreAI/services/user_service.py

@@ -0,0 +1,127 @@
+"""
+用户服务
+"""
+
+from typing import Optional, List
+from sqlalchemy.orm import Session
+from passlib.context import CryptContext
+from ..core.database import get_db
+from ..models.user import UserDB, User, UserCreate, UserUpdate
+from ..core.exceptions import UserNotFoundError, UserAlreadyExistsError
+
+pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+
+class UserService:
+    """用户服务类"""
+    
+    def __init__(self, db: Session):
+        self.db = db
+    
+    @staticmethod
+    def verify_password(plain_password: str, hashed_password: str) -> bool:
+        """验证密码"""
+        return pwd_context.verify(plain_password, hashed_password)
+    
+    @staticmethod
+    def get_password_hash(password: str) -> str:
+        """获取密码哈希"""
+        return pwd_context.hash(password)
+    
+    def get_user_by_id(self, user_id: int) -> Optional[User]:
+        """根据ID获取用户"""
+        user_db = self.db.query(UserDB).filter(UserDB.id == user_id).first()
+        if not user_db:
+            raise UserNotFoundError(f"User with id {user_id} not found")
+        return User.from_orm(user_db)
+    
+    def get_user_by_email(self, email: str) -> Optional[User]:
+        """根据邮箱获取用户"""
+        user_db = self.db.query(UserDB).filter(UserDB.email == email).first()
+        if not user_db:
+            raise UserNotFoundError(f"User with email {email} not found")
+        return User.from_orm(user_db)
+    
+    def get_user_by_username(self, username: str) -> Optional[User]:
+        """根据用户名获取用户"""
+        user_db = self.db.query(UserDB).filter(UserDB.username == username).first()
+        if not user_db:
+            raise UserNotFoundError(f"User with username {username} not found")
+        return User.from_orm(user_db)
+    
+    def create_user(self, user_create: UserCreate) -> User:
+        """创建用户"""
+        # 检查邮箱是否已存在
+        if self.db.query(UserDB).filter(UserDB.email == user_create.email).first():
+            raise UserAlreadyExistsError(f"Email {user_create.email} already registered")
+        
+        # 检查用户名是否已存在
+        if self.db.query(UserDB).filter(UserDB.username == user_create.username).first():
+            raise UserAlreadyExistsError(f"Username {user_create.username} already taken")
+        
+        # 创建新用户
+        hashed_password = self.get_password_hash(user_create.password)
+        user_db = UserDB(
+            username=user_create.username,
+            email=user_create.email,
+            hashed_password=hashed_password,
+            full_name=user_create.full_name,
+            institution=user_create.institution,
+            research_field=user_create.research_field
+        )
+        
+        self.db.add(user_db)
+        self.db.commit()
+        self.db.refresh(user_db)
+        
+        return User.from_orm(user_db)
+    
+    def update_user(self, user_id: int, user_update: UserUpdate) -> User:
+        """更新用户信息"""
+        user_db = self.db.query(UserDB).filter(UserDB.id == user_id).first()
+        if not user_db:
+            raise UserNotFoundError(f"User with id {user_id} not found")
+        
+        # 更新字段
+        update_data = user_update.dict(exclude_unset=True)
+        for field, value in update_data.items():
+            setattr(user_db, field, value)
+        
+        self.db.commit()
+        self.db.refresh(user_db)
+        
+        return User.from_orm(user_db)
+    
+    def delete_user(self, user_id: int) -> bool:
+        """删除用户"""
+        user_db = self.db.query(UserDB).filter(UserDB.id == user_id).first()
+        if not user_db:
+            raise UserNotFoundError(f"User with id {user_id} not found")
+        
+        self.db.delete(user_db)
+        self.db.commit()
+        
+        return True
+    
+    def authenticate_user(self, email: str, password: str) -> Optional[User]:
+        """验证用户登录"""
+        try:
+            user = self.get_user_by_email(email)
+            if self.verify_password(password, user.hashed_password):
+                return user
+        except UserNotFoundError:
+            pass
+        return None
+    
+    def change_password(self, user_id: int, current_password: str, new_password: str) -> bool:
+        """修改密码"""
+        user_db = self.db.query(UserDB).filter(UserDB.id == user_id).first()
+        if not user_db:
+            raise UserNotFoundError(f"User with id {user_id} not found")
+        
+        if not self.verify_password(current_password, user_db.hashed_password):
+            return False
+        
+        user_db.hashed_password = self.get_password_hash(new_password)
+        self.db.commit()
+        
+        return True

+ 278 - 0
Co-creation-projects/Apricity-InnocoreAI/services/writing_service.py

@@ -0,0 +1,278 @@
+"""
+写作服务
+"""
+
+from typing import Optional, List, Dict, Any
+from sqlalchemy.orm import Session
+from ..core.database import get_db
+from ..models.writing import WritingDB, Writing, WritingCreate, WritingUpdate
+from ..core.exceptions import WritingNotFoundError
+from ..services.paper_service import PaperService
+from ..utils.citation_formatter import CitationFormatter
+import json
+import re
+
+class WritingService:
+    """写作服务类"""
+    
+    def __init__(self, db: Session):
+        self.db = db
+        self.paper_service = PaperService(db)
+        self.citation_formatter = CitationFormatter()
+    
+    def get_writing_by_id(self, writing_id: int) -> Optional[Writing]:
+        """根据ID获取写作"""
+        writing_db = self.db.query(WritingDB).filter(WritingDB.id == writing_id).first()
+        if not writing_db:
+            raise WritingNotFoundError(f"Writing with id {writing_id} not found")
+        return Writing.from_orm(writing_db)
+    
+    def get_writings_by_user(self, user_id: int, skip: int = 0, limit: int = 20) -> List[Writing]:
+        """获取用户的写作列表"""
+        writings_db = self.db.query(WritingDB).filter(
+            WritingDB.user_id == user_id
+        ).order_by(WritingDB.created_at.desc()).offset(skip).limit(limit).all()
+        return [Writing.from_orm(writing) for writing in writings_db]
+    
+    def create_writing(self, writing_create: WritingCreate, user_id: int, task_id: Optional[int] = None) -> Writing:
+        """创建写作"""
+        # 计算字数
+        content = writing_create.content or ""
+        word_count = len(re.findall(r'\S+', content))
+        
+        writing_db = WritingDB(
+            title=writing_create.title,
+            writing_type=writing_create.writing_type,
+            content=content,
+            outline=json.dumps(writing_create.outline or []),
+            paper_ids=json.dumps(writing_create.paper_ids),
+            word_count=word_count,
+            user_id=user_id,
+            task_id=task_id
+        )
+        
+        self.db.add(writing_db)
+        self.db.commit()
+        self.db.refresh(writing_db)
+        
+        return Writing.from_orm(writing_db)
+    
+    def update_writing(self, writing_id: int, writing_update: WritingUpdate) -> Writing:
+        """更新写作"""
+        writing_db = self.db.query(WritingDB).filter(WritingDB.id == writing_id).first()
+        if not writing_db:
+            raise WritingNotFoundError(f"Writing with id {writing_id} not found")
+        
+        # 更新字段
+        update_data = writing_update.dict(exclude_unset=True)
+        for field, value in update_data.items():
+            if field in ['outline', 'sections', 'citations', 'paper_ids']:
+                setattr(writing_db, field, json.dumps(value))
+            else:
+                setattr(writing_db, field, value)
+        
+        # 重新计算字数
+        if 'content' in update_data:
+            writing_db.word_count = len(re.findall(r'\S+', writing_db.content or ""))
+        
+        self.db.commit()
+        self.db.refresh(writing_db)
+        
+        return Writing.from_orm(writing_db)
+    
+    def delete_writing(self, writing_id: int) -> bool:
+        """删除写作"""
+        writing_db = self.db.query(WritingDB).filter(WritingDB.id == writing_id).first()
+        if not writing_db:
+            raise WritingNotFoundError(f"Writing with id {writing_id} not found")
+        
+        self.db.delete(writing_db)
+        self.db.commit()
+        
+        return True
+    
+    def get_writing_statistics(self, user_id: int) -> Dict[str, Any]:
+        """获取写作统计信息"""
+        total_writings = self.db.query(WritingDB).filter(WritingDB.user_id == user_id).count()
+        
+        # 按类型统计
+        type_stats = self.db.query(
+            WritingDB.writing_type,
+            self.db.func.count(WritingDB.id)
+        ).filter(WritingDB.user_id == user_id).group_by(WritingDB.writing_type).all()
+        
+        # 按状态统计
+        status_stats = self.db.query(
+            WritingDB.status,
+            self.db.func.count(WritingDB.id)
+        ).filter(WritingDB.user_id == user_id).group_by(WritingDB.status).all()
+        
+        # 总字数
+        total_words = self.db.query(
+            self.db.func.sum(WritingDB.word_count)
+        ).filter(WritingDB.user_id == user_id).scalar() or 0
+        
+        # 平均质量分数
+        avg_quality = self.db.query(
+            self.db.func.avg(WritingDB.quality_score)
+        ).filter(WritingDB.user_id == user_id).scalar() or 0
+        
+        return {
+            'total_writings': total_writings,
+            'total_words': int(total_words),
+            'average_quality': float(avg_quality),
+            'type_distribution': dict(type_stats),
+            'status_distribution': dict(status_stats)
+        }
+    
+    def format_citations(self, writing_id: int, style: str = "APA") -> Dict[str, Any]:
+        """格式化引用"""
+        writing = self.get_writing_by_id(writing_id)
+        
+        # 获取参考论文
+        paper_ids = json.loads(writing.paper_ids or "[]")
+        papers = []
+        for paper_id in paper_ids:
+            try:
+                paper = self.paper_service.get_paper_by_id(paper_id)
+                papers.append(paper)
+            except Exception:
+                continue
+        
+        # 格式化引用
+        formatted_citations = self.citation_formatter.format_papers(papers, style)
+        
+        # 更新写作中的引用
+        citations_data = [
+            {
+                'id': paper.id,
+                'title': paper.title,
+                'authors': paper.authors,
+                'journal': paper.journal,
+                'year': paper.publication_year,
+                'formatted': formatted_citations.get(str(paper.id), "")
+            }
+            for paper in papers
+        ]
+        
+        self.update_writing(writing_id, WritingUpdate(citations=citations_data))
+        
+        return {
+            'citations': citations_data,
+            'bibliography': self.citation_formatter.generate_bibliography(papers, style)
+        }
+    
+    def export_writing(self, writing_id: int, format: str = "markdown", include_citations: bool = True) -> Dict[str, Any]:
+        """导出写作"""
+        writing = self.get_writing_by_id(writing_id)
+        
+        if format == "markdown":
+            return self._export_to_markdown(writing, include_citations)
+        elif format == "latex":
+            return self._export_to_latex(writing, include_citations)
+        elif format == "docx":
+            return self._export_to_docx(writing, include_citations)
+        elif format == "pdf":
+            return self._export_to_pdf(writing, include_citations)
+        else:
+            raise ValueError(f"Unsupported export format: {format}")
+    
+    def _export_to_markdown(self, writing: Writing, include_citations: bool = True) -> str:
+        """导出为Markdown格式"""
+        markdown = f"# {writing.title}\n\n"
+        markdown += f"**类型**: {writing.writing_type}\n\n"
+        markdown += f"**字数**: {writing.word_count}\n\n"
+        markdown += f"**创建时间**: {writing.created_at.strftime('%Y-%m-%d %H:%M:%S')}\n\n"
+        
+        if writing.content:
+            markdown += f"{writing.content}\n\n"
+        
+        if include_citations and writing.citations:
+            markdown += "## 参考文献\n\n"
+            for citation in writing.citations:
+                markdown += f"- {citation.get('formatted', '')}\n"
+        
+        return markdown
+    
+    def _export_to_latex(self, writing: Writing, include_citations: bool = True) -> str:
+        """导出为LaTeX格式"""
+        latex = "\\documentclass{article}\n"
+        latex += "\\usepackage[utf8]{inputenc}\n"
+        latex += "\\usepackage{cite}\n\n"
+        latex += "\\begin{document}\n\n"
+        latex += f"\\title{{{writing.title}}}\n"
+        latex += "\\maketitle\n\n"
+        
+        if writing.content:
+            # 简单的Markdown到LaTeX转换
+            content = writing.content.replace("**", "\\textbf{").replace("**", "}")
+            content = content.replace("*", "\\textit{").replace("*", "}")
+            latex += f"{content}\n\n"
+        
+        if include_citations and writing.citations:
+            latex += "\\bibliographystyle{plain}\n"
+            latex += "\\bibliography{references}\n"
+        
+        latex += "\\end{document}"
+        
+        return latex
+    
+    def _export_to_docx(self, writing: Writing, include_citations: bool = True) -> bytes:
+        """导出为Word文档格式"""
+        # 这里可以使用python-docx库
+        # 暂时返回Markdown内容的字节
+        content = self._export_to_markdown(writing, include_citations)
+        return content.encode('utf-8')
+    
+    def _export_to_pdf(self, writing: Writing, include_citations: bool = True) -> bytes:
+        """导出为PDF格式"""
+        # 这里可以使用reportlab或其他PDF生成库
+        # 暂时返回Markdown内容的字节
+        content = self._export_to_markdown(writing, include_citations)
+        return content.encode('utf-8')
+    
+    def generate_outline(self, topic: str, paper_ids: List[int], writing_type: str = "review") -> List[Dict[str, Any]]:
+        """生成写作大纲"""
+        # 获取参考论文
+        papers = []
+        for paper_id in paper_ids:
+            try:
+                paper = self.paper_service.get_paper_by_id(paper_id)
+                papers.append(paper)
+            except Exception:
+                continue
+        
+        # 根据写作类型生成大纲模板
+        if writing_type == "review":
+            outline = [
+                {"title": "引言", "level": 1, "content": "研究背景和意义"},
+                {"title": "文献综述", "level": 1, "content": "相关研究工作总结"},
+                {"title": "方法论分析", "level": 1, "content": "研究方法比较"},
+                {"title": "主要发现", "level": 1, "content": "研究成果总结"},
+                {"title": "讨论与展望", "level": 1, "content": "研究局限性和未来方向"},
+                {"title": "结论", "level": 1, "content": "研究总结"}
+            ]
+        elif writing_type == "summary":
+            outline = [
+                {"title": "研究背景", "level": 1, "content": "研究动机和目标"},
+                {"title": "研究方法", "level": 1, "content": "实验设计和数据分析"},
+                {"title": "主要结果", "level": 1, "content": "关键发现和数据分析"},
+                {"title": "结论与意义", "level": 1, "content": "研究贡献和影响"}
+            ]
+        elif writing_type == "critique":
+            outline = [
+                {"title": "论文概述", "level": 1, "content": "研究内容总结"},
+                {"title": "优点分析", "level": 1, "content": "研究的创新性和贡献"},
+                {"title": "问题与局限", "level": 1, "content": "研究中存在的问题"},
+                {"title": "改进建议", "level": 1, "content": "对研究的改进意见"}
+            ]
+        else:
+            outline = [
+                {"title": "研究背景", "level": 1, "content": ""},
+                {"title": "研究目标", "level": 1, "content": ""},
+                {"title": "研究方法", "level": 1, "content": ""},
+                {"title": "预期结果", "level": 1, "content": ""},
+                {"title": "研究意义", "level": 1, "content": ""}
+            ]
+        
+        return outline

+ 56 - 0
Co-creation-projects/Apricity-InnocoreAI/setup.py

@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+"""
+InnoCore AI - Simple Setup Script
+"""
+
+import subprocess
+import sys
+from pathlib import Path
+
+def main():
+    print("InnoCore AI - Quick Setup")
+    print("=" * 30)
+    
+    # Install basic dependencies without version conflicts
+    basic_deps = [
+        "fastapi",
+        "uvicorn[standard]",
+        "python-multipart",
+        "python-dotenv"
+    ]
+    
+    print("Installing basic dependencies...")
+    for dep in basic_deps:
+        try:
+            subprocess.check_call([sys.executable, "-m", "pip", "install", dep])
+            print(f"[OK] {dep}")
+        except subprocess.CalledProcessError:
+            print(f"[SKIP] {dep} (may already exist)")
+    
+    # Create .env file
+    env_file = Path(".env")
+    if not env_file.exists():
+        env_content = """# InnoCore AI Configuration
+OPENAI_API_KEY=your_openai_api_key_here
+DATABASE_URL=sqlite:///./innocore.db
+SECRET_KEY=your_secret_key_here_change_this_in_production
+DEBUG=True
+"""
+        env_file.write_text(env_content)
+        print("[OK] .env file created")
+    else:
+        print("[OK] .env file exists")
+    
+    # Create directories
+    Path("data").mkdir(exist_ok=True)
+    Path("logs").mkdir(exist_ok=True)
+    print("[OK] Directories created")
+    
+    print("\n[SUCCESS] Setup completed!")
+    print("Next steps:")
+    print("1. Edit .env file and add your OpenAI API key")
+    print("2. Run: python run.py")
+    print("3. Open: http://localhost:8000")
+
+if __name__ == "__main__":
+    main()

+ 15 - 0
Co-creation-projects/Apricity-InnocoreAI/utils/__init__.py

@@ -0,0 +1,15 @@
+"""
+InnoCore AI 工具模块
+"""
+
+from .pdf_parser import PDFParser
+from .embedding import EmbeddingGenerator
+from .text_processor import TextProcessor
+from .citation_formatter import CitationFormatter
+
+__all__ = [
+    "PDFParser",
+    "EmbeddingGenerator", 
+    "TextProcessor",
+    "CitationFormatter"
+]

+ 526 - 0
Co-creation-projects/Apricity-InnocoreAI/utils/citation_formatter.py

@@ -0,0 +1,526 @@
+"""
+InnoCore AI 引用格式化工具
+"""
+
+import re
+from typing import Dict, List, Optional, Any
+from datetime import datetime
+
+class CitationFormatter:
+    """引用格式化器"""
+    
+    def __init__(self):
+        self.month_names = {
+            1: "Jan", 2: "Feb", 3: "Mar", 4: "Apr", 5: "May", 6: "Jun",
+            7: "Jul", 8: "Aug", 9: "Sep", 10: "Oct", 11: "Nov", 12: "Dec"
+        }
+    
+    def format_bibtex(self, paper_info: Dict[str, Any]) -> str:
+        """格式化为BibTeX"""
+        # 生成引用键
+        citation_key = self._generate_citation_key(paper_info)
+        
+        # 确定条目类型
+        entry_type = self._determine_entry_type(paper_info)
+        
+        # 构建BibTeX条目
+        bibtex_lines = [f"@{entry_type}{{{citation_key}"]
+        
+        # 添加作者
+        authors = paper_info.get("authors", [])
+        if authors:
+            formatted_authors = self._format_bibtex_authors(authors)
+            bibtex_lines.append(f"  author = {{{formatted_authors}}}")
+        
+        # 添加标题
+        title = paper_info.get("title", "")
+        if title:
+            bibtex_lines.append(f"  title = {{{title}}}")
+        
+        # 添加期刊/会议信息
+        if entry_type == "article":
+            journal = paper_info.get("journal", "")
+            if journal:
+                bibtex_lines.append(f"  journal = {{{journal}}}")
+            
+            volume = paper_info.get("volume", "")
+            if volume:
+                bibtex_lines.append(f"  volume = {{{volume}}}")
+            
+            number = paper_info.get("number", "")
+            if number:
+                bibtex_lines.append(f"  number = {{{number}}}")
+            
+            pages = paper_info.get("pages", "")
+            if pages:
+                bibtex_lines.append(f"  pages = {{{pages}}}")
+        
+        elif entry_type == "inproceedings":
+            booktitle = paper_info.get("booktitle", "")
+            if booktitle:
+                bibtex_lines.append(f"  booktitle = {{{booktitle}}}")
+            
+            pages = paper_info.get("pages", "")
+            if pages:
+                bibtex_lines.append(f"  pages = {{{pages}}}")
+        
+        elif entry_type == "book":
+            publisher = paper_info.get("publisher", "")
+            if publisher:
+                bibtex_lines.append(f"  publisher = {{{publisher}}}")
+        
+        # 添加年份
+        year = paper_info.get("year", "")
+        if year:
+            bibtex_lines.append(f"  year = {{{year}}}")
+        
+        # 添加月份
+        month = paper_info.get("month", "")
+        if month:
+            bibtex_lines.append(f"  month = {{{month}}}")
+        
+        # 添加DOI
+        doi = paper_info.get("doi", "")
+        if doi:
+            bibtex_lines.append(f"  doi = {{{doi}}}")
+        
+        # 添加URL
+        url = paper_info.get("url", "")
+        if url:
+            bibtex_lines.append(f"  url = {{{url}}}")
+        
+        # 添加笔记
+        note = paper_info.get("note", "")
+        if note:
+            bibtex_lines.append(f"  note = {{{note}}}")
+        
+        # 关闭条目
+        bibtex_lines.append("}")
+        
+        return "\n".join(bibtex_lines)
+    
+    def format_apa(self, paper_info: Dict[str, Any]) -> str:
+        """格式化为APA格式"""
+        authors = paper_info.get("authors", [])
+        year = paper_info.get("year", "")
+        title = paper_info.get("title", "")
+        
+        # 格式化作者
+        author_text = self._format_apa_authors(authors)
+        
+        # 构建基本引用
+        if year:
+            citation = f"{author_text} ({year}). {title}."
+        else:
+            citation = f"{author_text}. {title}."
+        
+        # 添加期刊信息
+        journal = paper_info.get("journal", "")
+        volume = paper_info.get("volume", "")
+        number = paper_info.get("number", "")
+        pages = paper_info.get("pages", "")
+        
+        if journal:
+            if volume and number:
+                citation += f" *{journal}*, *{volume}({number})*"
+            elif volume:
+                citation += f" *{journal}*, *{volume}*"
+            else:
+                citation += f" *{journal}*"
+            
+            if pages:
+                citation += f", {pages}."
+            else:
+                citation += "."
+        
+        # 添加书籍信息
+        publisher = paper_info.get("publisher", "")
+        if publisher:
+            citation += f" {publisher}."
+        
+        # 添加会议信息
+        booktitle = paper_info.get("booktitle", "")
+        if booktitle:
+            citation += f" In *{booktitle}*"
+            if pages:
+                citation += f" (pp. {pages})."
+            else:
+                citation += "."
+        
+        # 添加DOI
+        doi = paper_info.get("doi", "")
+        if doi:
+            citation += f" https://doi.org/{doi}"
+        
+        return citation
+    
+    def format_ieee(self, paper_info: Dict[str, Any]) -> str:
+        """格式化为IEEE格式"""
+        authors = paper_info.get("authors", [])
+        year = paper_info.get("year", "")
+        title = paper_info.get("title", "")
+        
+        # 格式化作者(IEEE格式)
+        author_text = self._format_ieee_authors(authors)
+        
+        # 构建基本引用
+        citation = f'{author_text}, "{title},"'
+        
+        # 添加期刊信息
+        journal = paper_info.get("journal", "")
+        volume = paper_info.get("volume", "")
+        number = paper_info.get("number", "")
+        pages = paper_info.get("pages", "")
+        
+        if journal:
+            if volume and number:
+                citation += f" *{journal}*, vol. {volume}, no. {number}"
+            elif volume:
+                citation += f" *{journal}*, vol. {volume}"
+            else:
+                citation += f" *{journal}*"
+            
+            if pages:
+                citation += f", pp. {pages}"
+        
+        # 添加会议信息
+        booktitle = paper_info.get("booktitle", "")
+        if booktitle:
+            citation += f" in *{booktitle}*"
+            if pages:
+                citation += f", pp. {pages}"
+        
+        # 添加书籍信息
+        publisher = paper_info.get("publisher", "")
+        if publisher:
+            citation += f" {publisher}"
+        
+        # 添加年份和月份
+        month = paper_info.get("month", "")
+        if year:
+            if month:
+                citation += f", {month}. {year}."
+            else:
+                citation += f", {year}."
+        
+        # 添加DOI
+        doi = paper_info.get("doi", "")
+        if doi:
+            citation += f" doi: {doi}"
+        
+        return citation
+    
+    def format_mla(self, paper_info: Dict[str, Any]) -> str:
+        """格式化为MLA格式"""
+        authors = paper_info.get("authors", [])
+        title = paper_info.get("title", "")
+        journal = paper_info.get("journal", "")
+        year = paper_info.get("year", "")
+        pages = paper_info.get("pages", "")
+        
+        # 格式化作者(MLA格式)
+        author_text = self._format_mla_authors(authors)
+        
+        # 构建基本引用
+        if author_text:
+            citation = f'{author_text}. "{title}."'
+        else:
+            citation = f'"{title}."'
+        
+        # 添加期刊信息
+        if journal:
+            citation += f" *{journal}*"
+            
+            if volume and number:
+                citation += f", vol. {volume}, no. {number}"
+            elif volume:
+                citation += f", vol. {volume}"
+            
+            if year:
+                citation += f", {year}"
+            
+            if pages:
+                citation += f", pp. {pages}."
+            else:
+                citation += "."
+        
+        # 添加书籍信息
+        publisher = paper_info.get("publisher", "")
+        if publisher:
+            citation += f" {publisher}"
+            if year:
+                citation += f", {year}."
+            else:
+                citation += "."
+        
+        return citation
+    
+    def format_chicago(self, paper_info: Dict[str, Any]) -> str:
+        """格式化为Chicago格式"""
+        authors = paper_info.get("authors", [])
+        title = paper_info.get("title", "")
+        journal = paper_info.get("journal", "")
+        volume = paper_info.get("volume", "")
+        number = paper_info.get("number", "")
+        year = paper_info.get("year", "")
+        pages = paper_info.get("pages", "")
+        
+        # 格式化作者(Chicago格式)
+        author_text = self._format_chicago_authors(authors)
+        
+        # 构建基本引用
+        if author_text:
+            citation = f'{author_text}. "{title}."'
+        else:
+            citation = f'"{title}."'
+        
+        # 添加期刊信息
+        if journal:
+            citation += f" *{journal}*"
+            
+            if volume and number:
+                citation += f" {volume}, no. {number}"
+            elif volume:
+                citation += f" {volume}"
+            
+            if year:
+                citation += f" ({year})"
+            
+            if pages:
+                citation += f": {pages}."
+            else:
+                citation += "."
+        
+        return citation
+    
+    def _generate_citation_key(self, paper_info: Dict[str, Any]) -> str:
+        """生成引用键"""
+        # 获取第一作者的姓氏
+        authors = paper_info.get("authors", [])
+        if authors:
+            first_author = authors[0]
+            if isinstance(first_author, str):
+                last_name = first_author.split()[-1].lower()
+            else:
+                last_name = "unknown"
+        else:
+            last_name = "unknown"
+        
+        # 获取年份
+        year = str(paper_info.get("year", datetime.now().year))
+        
+        # 获取标题关键词
+        title = paper_info.get("title", "")
+        title_words = re.findall(r'\b[a-zA-Z]{3,}\b', title.lower())[:3]
+        title_key = "".join(title_words)
+        
+        return f"{last_name}{year}{title_key}"
+    
+    def _determine_entry_type(self, paper_info: Dict[str, Any]) -> str:
+        """确定BibTeX条目类型"""
+        if paper_info.get("journal"):
+            return "article"
+        elif paper_info.get("booktitle"):
+            return "inproceedings"
+        elif paper_info.get("publisher"):
+            return "book"
+        else:
+            return "misc"
+    
+    def _format_bibtex_authors(self, authors: List[str]) -> str:
+        """格式化BibTeX作者"""
+        formatted_authors = []
+        
+        for author in authors:
+            if isinstance(author, str):
+                # 将 "First Last" 转换为 "Last, First"
+                parts = author.split()
+                if len(parts) >= 2:
+                    last_name = parts[-1]
+                    first_names = " ".join(parts[:-1])
+                    formatted_authors.append(f"{last_name}, {first_names}")
+                else:
+                    formatted_authors.append(author)
+            else:
+                formatted_authors.append(str(author))
+        
+        return " and ".join(formatted_authors)
+    
+    def _format_apa_authors(self, authors: List[str]) -> str:
+        """格式化APA作者"""
+        if not authors:
+            return ""
+        
+        if len(authors) == 1:
+            return authors[0]
+        elif len(authors) == 2:
+            return f"{authors[0]} & {authors[1]}"
+        elif len(authors) <= 20:
+            return ", ".join(authors[:-1]) + f", & {authors[-1]}"
+        else:
+            return ", ".join(authors[:19]) + f", ... {authors[-1]}"
+    
+    def _format_ieee_authors(self, authors: List[str]) -> str:
+        """格式化IEEE作者"""
+        formatted_authors = []
+        
+        for i, author in enumerate(authors[:3]):  # IEEE通常只列出前3个作者
+            if isinstance(author, str):
+                parts = author.split()
+                if len(parts) >= 2:
+                    # 转换为 "F. Last" 格式
+                    initials = " ".join([f"{p[0]}." for p in parts[:-1]])
+                    last_name = parts[-1]
+                    formatted_authors.append(f"{initials} {last_name}")
+                else:
+                    formatted_authors.append(author)
+            else:
+                formatted_authors.append(str(author))
+        
+        if len(authors) > 3:
+            formatted_authors.append("et al.")
+        
+        return ", ".join(formatted_authors)
+    
+    def _format_mla_authors(self, authors: List[str]) -> str:
+        """格式化MLA作者"""
+        if not authors:
+            return ""
+        
+        if len(authors) == 1:
+            return authors[0]
+        elif len(authors) == 2:
+            return f"{authors[0]} and {authors[1]}"
+        else:
+            return f"{authors[0]}, et al."
+    
+    def _format_chicago_authors(self, authors: List[str]) -> str:
+        """格式化Chicago作者"""
+        if not authors:
+            return ""
+        
+        if len(authors) == 1:
+            return authors[0]
+        elif len(authors) == 2:
+            return f"{authors[0]} and {authors[1]}"
+        else:
+            return f"{authors[0]}, et al."
+    
+    def parse_bibtex(self, bibtex_text: str) -> Dict[str, Any]:
+        """解析BibTeX文本"""
+        paper_info = {}
+        
+        # 提取条目类型和键
+        entry_match = re.match(r'@(\w+)\{([^,]+),', bibtex_text)
+        if entry_match:
+            paper_info["entry_type"] = entry_match.group(1)
+            paper_info["citation_key"] = entry_match.group(2)
+        
+        # 提取字段
+        field_pattern = r'\s*(\w+)\s*=\s*\{([^}]*)\}'
+        matches = re.findall(field_pattern, bibtex_text)
+        
+        for field_name, field_value in matches:
+            paper_info[field_name] = field_value
+        
+        return paper_info
+    
+    def validate_citation(self, citation: str, style: str) -> Dict[str, Any]:
+        """验证引用格式"""
+        validation_result = {
+            "is_valid": True,
+            "errors": [],
+            "warnings": [],
+            "suggestions": []
+        }
+        
+        if style.lower() == "bibtex":
+            validation_result = self._validate_bibtex(citation, validation_result)
+        elif style.lower() == "apa":
+            validation_result = self._validate_apa(citation, validation_result)
+        elif style.lower() == "ieee":
+            validation_result = self._validate_ieee(citation, validation_result)
+        
+        return validation_result
+    
+    def _validate_bibtex(self, citation: str, result: Dict[str, Any]) -> Dict[str, Any]:
+        """验证BibTeX格式"""
+        # 检查基本结构
+        if not citation.startswith('@'):
+            result["is_valid"] = False
+            result["errors"].append("BibTeX必须以@开头")
+        
+        if not citation.endswith('}'):
+            result["is_valid"] = False
+            result["errors"].append("BibTeX必须以}结尾")
+        
+        # 检查必需字段
+        if 'title' not in citation:
+            result["warnings"].append("缺少title字段")
+        
+        if 'author' not in citation:
+            result["warnings"].append("缺少author字段")
+        
+        if 'year' not in citation:
+            result["warnings"].append("缺少year字段")
+        
+        return result
+    
+    def _validate_apa(self, citation: str, result: Dict[str, Any]) -> Dict[str, Any]:
+        """验证APA格式"""
+        # 检查作者格式
+        if '(' in citation and ')' in citation:
+            year_pattern = r'\((\d{4})\)'
+            if not re.search(year_pattern, citation):
+                result["warnings"].append("APA格式应包含出版年份")
+        
+        # 检查标题格式
+        if not citation.strip().endswith('.'):
+            result["warnings"].append("APA引用应以句号结尾")
+        
+        return result
+    
+    def _validate_ieee(self, citation: str, result: Dict[str, Any]) -> Dict[str, Any]:
+        """验证IEEE格式"""
+        # 检查引用格式
+        if '"' not in citation:
+            result["warnings"].append("IEEE格式中标题应使用双引号")
+        
+        # 检查期刊格式
+        if '*' not in citation:
+            result["warnings"].append("IEEE格式中期刊名应使用斜体(*)")
+        
+        return result
+    
+    def convert_between_formats(self, citation: str, from_style: str, to_style: str) -> str:
+        """在不同格式间转换引用"""
+        try:
+            # 解析原始格式
+            if from_style.lower() == "bibtex":
+                paper_info = self.parse_bibtex(citation)
+            else:
+                # 对于其他格式,需要更复杂的解析逻辑
+                # 这里提供简化实现
+                paper_info = {
+                    "title": "",
+                    "authors": [],
+                    "year": "",
+                    "journal": ""
+                }
+            
+            # 转换为目标格式
+            if to_style.lower() == "bibtex":
+                return self.format_bibtex(paper_info)
+            elif to_style.lower() == "apa":
+                return self.format_apa(paper_info)
+            elif to_style.lower() == "ieee":
+                return self.format_ieee(paper_info)
+            elif to_style.lower() == "mla":
+                return self.format_mla(paper_info)
+            elif to_style.lower() == "chicago":
+                return self.format_chicago(paper_info)
+            else:
+                return citation
+                
+        except Exception as e:
+            return f"转换失败: {str(e)}"

+ 309 - 0
Co-creation-projects/Apricity-InnocoreAI/utils/embedding.py

@@ -0,0 +1,309 @@
+"""
+InnoCore AI 向量生成工具
+"""
+
+import asyncio
+from typing import List, Dict, Optional, Any
+import numpy as np
+from openai import AsyncOpenAI
+import hashlib
+import json
+
+from ..core.config import get_config
+from ..core.exceptions import AgentException
+
+class EmbeddingGenerator:
+    """向量生成器"""
+    
+    def __init__(self):
+        self.config = get_config()
+        self.client = None
+        self.embedding_model = self.config.vector_db.embedding_model
+        self.cache = {}  # 简单的内存缓存
+    
+    async def initialize(self):
+        """初始化向量生成器"""
+        try:
+            self.client = AsyncOpenAI(
+                api_key=self.config.llm.api_key,
+                base_url=self.config.llm.base_url
+            )
+        except Exception as e:
+            raise AgentException(f"向量生成器初始化失败: {str(e)}")
+    
+    async def generate_embedding(self, text: str, use_cache: bool = True) -> List[float]:
+        """生成文本向量"""
+        if not text:
+            return [0.0] * 1536  # 返回零向量
+        
+        # 检查缓存
+        if use_cache:
+            cache_key = self._get_cache_key(text)
+            if cache_key in self.cache:
+                return self.cache[cache_key]
+        
+        try:
+            # 清理文本
+            cleaned_text = self._clean_text(text)
+            
+            # 调用OpenAI API
+            response = await self.client.embeddings.create(
+                model=self.embedding_model,
+                input=cleaned_text
+            )
+            
+            embedding = response.data[0].embedding
+            
+            # 缓存结果
+            if use_cache:
+                cache_key = self._get_cache_key(text)
+                self.cache[cache_key] = embedding
+            
+            return embedding
+            
+        except Exception as e:
+            raise AgentException(f"向量生成失败: {str(e)}")
+    
+    async def generate_batch_embeddings(self, texts: List[str], 
+                                       batch_size: int = 10) -> List[List[float]]:
+        """批量生成向量"""
+        embeddings = []
+        
+        for i in range(0, len(texts), batch_size):
+            batch = texts[i:i + batch_size]
+            
+            try:
+                # 批量调用API
+                cleaned_texts = [self._clean_text(text) for text in batch]
+                
+                response = await self.client.embeddings.create(
+                    model=self.embedding_model,
+                    input=cleaned_texts
+                )
+                
+                batch_embeddings = [item.embedding for item in response.data]
+                embeddings.extend(batch_embeddings)
+                
+            except Exception as e:
+                # 如果批量失败,逐个生成
+                for text in batch:
+                    try:
+                        embedding = await self.generate_embedding(text)
+                        embeddings.append(embedding)
+                    except Exception as single_error:
+                        print(f"单个向量生成失败: {str(single_error)}")
+                        embeddings.append([0.0] * 1536)  # 零向量
+        
+        return embeddings
+    
+    async def generate_paper_embedding(self, paper_info: Dict[str, Any]) -> List[float]:
+        """为论文生成综合向量"""
+        # 组合论文的关键信息
+        title = paper_info.get("title", "")
+        abstract = paper_info.get("abstract", "")
+        authors = " ".join(paper_info.get("authors", []))
+        
+        # 构建综合文本
+        combined_text = f"{title} {abstract} {authors}"
+        
+        # 如果有结构化内容,也包含进来
+        sections = paper_info.get("sections", {})
+        if sections:
+            section_text = " ".join(sections.values())
+            combined_text += " " + section_text
+        
+        return await self.generate_embedding(combined_text)
+    
+    async def generate_section_embeddings(self, sections: Dict[str, str]) -> Dict[str, List[float]]:
+        """为各个章节生成向量"""
+        section_embeddings = {}
+        
+        for section_name, section_content in sections.items():
+            if section_content.strip():
+                try:
+                    embedding = await self.generate_embedding(section_content)
+                    section_embeddings[section_name] = embedding
+                except Exception as e:
+                    print(f"章节 {section_name} 向量生成失败: {str(e)}")
+                    section_embeddings[section_name] = [0.0] * 1536
+        
+        return section_embeddings
+    
+    def _clean_text(self, text: str) -> str:
+        """清理文本"""
+        if not text:
+            return ""
+        
+        # 移除多余的空白字符
+        text = ' '.join(text.split())
+        
+        # 截断过长的文本(OpenAI有token限制)
+        max_length = 8000  # 保守估计
+        if len(text) > max_length:
+            text = text[:max_length]
+        
+        return text
+    
+    def _get_cache_key(self, text: str) -> str:
+        """生成缓存键"""
+        return hashlib.md5(text.encode()).hexdigest()
+    
+    def clear_cache(self):
+        """清空缓存"""
+        self.cache.clear()
+    
+    def get_cache_size(self) -> int:
+        """获取缓存大小"""
+        return len(self.cache)
+    
+    async def calculate_similarity(self, text1: str, text2: str) -> float:
+        """计算两个文本的相似度"""
+        try:
+            embedding1 = await self.generate_embedding(text1)
+            embedding2 = await self.generate_embedding(text2)
+            
+            return self._cosine_similarity(embedding1, embedding2)
+            
+        except Exception as e:
+            print(f"相似度计算失败: {str(e)}")
+            return 0.0
+    
+    def _cosine_similarity(self, vec1: List[float], vec2: List[float]) -> float:
+        """计算余弦相似度"""
+        if len(vec1) != len(vec2):
+            return 0.0
+        
+        try:
+            vec1_np = np.array(vec1)
+            vec2_np = np.array(vec2)
+            
+            dot_product = np.dot(vec1_np, vec2_np)
+            norm1 = np.linalg.norm(vec1_np)
+            norm2 = np.linalg.norm(vec2_np)
+            
+            if norm1 == 0 or norm2 == 0:
+                return 0.0
+            
+            return dot_product / (norm1 * norm2)
+            
+        except Exception:
+            return 0.0
+    
+    async def find_most_similar(self, query_text: str, 
+                               candidate_texts: List[str],
+                               top_k: int = 5) -> List[Dict[str, Any]]:
+        """找到最相似的文本"""
+        if not candidate_texts:
+            return []
+        
+        try:
+            # 生成查询向量
+            query_embedding = await self.generate_embedding(query_text)
+            
+            # 生成候选文本向量
+            candidate_embeddings = await self.generate_batch_embeddings(candidate_texts)
+            
+            # 计算相似度
+            similarities = []
+            for i, candidate_embedding in enumerate(candidate_embeddings):
+                similarity = self._cosine_similarity(query_embedding, candidate_embedding)
+                similarities.append({
+                    "text": candidate_texts[i],
+                    "similarity": similarity,
+                    "index": i
+                })
+            
+            # 按相似度排序
+            similarities.sort(key=lambda x: x["similarity"], reverse=True)
+            
+            return similarities[:top_k]
+            
+        except Exception as e:
+            print(f"相似文本查找失败: {str(e)}")
+            return []
+    
+    async def cluster_texts(self, texts: List[str], 
+                          num_clusters: int = 3) -> Dict[str, Any]:
+        """文本聚类(简化实现)"""
+        try:
+            # 生成所有文本的向量
+            embeddings = await self.generate_batch_embeddings(texts)
+            
+            # 简单的聚类逻辑(基于相似度阈值)
+            clusters = {}
+            cluster_id = 0
+            used_indices = set()
+            
+            for i, embedding in enumerate(embeddings):
+                if i in used_indices:
+                    continue
+                
+                # 创建新聚类
+                clusters[f"cluster_{cluster_id}"] = {
+                    "texts": [texts[i]],
+                    "indices": [i],
+                    "center": embedding
+                }
+                used_indices.add(i)
+                
+                # 查找相似文本加入同一聚类
+                for j, other_embedding in enumerate(embeddings):
+                    if j in used_indices:
+                        continue
+                    
+                    similarity = self._cosine_similarity(embedding, other_embedding)
+                    if similarity > 0.8:  # 相似度阈值
+                        clusters[f"cluster_{cluster_id}"]["texts"].append(texts[j])
+                        clusters[f"cluster_{cluster_id}"]["indices"].append(j)
+                        used_indices.add(j)
+                
+                cluster_id += 1
+            
+            return {
+                "clusters": clusters,
+                "num_clusters": len(clusters),
+                "total_texts": len(texts)
+            }
+            
+        except Exception as e:
+            print(f"文本聚类失败: {str(e)}")
+            return {"clusters": {}, "num_clusters": 0, "total_texts": len(texts)}
+    
+    async def extract_keywords(self, text: str, max_keywords: int = 10) -> List[str]:
+        """提取关键词(基于TF-IDF的简化实现)"""
+        try:
+            # 分词
+            words = text.lower().split()
+            
+            # 过滤停用词(简化版)
+            stop_words = {
+                'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
+                'of', 'with', 'by', 'is', 'are', 'was', 'were', 'be', 'been', 'have',
+                'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should'
+            }
+            
+            filtered_words = [word for word in words if word not in stop_words and len(word) > 2]
+            
+            # 计算词频
+            word_freq = {}
+            for word in filtered_words:
+                word_freq[word] = word_freq.get(word, 0) + 1
+            
+            # 按频率排序
+            sorted_words = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)
+            
+            # 返回前N个关键词
+            return [word for word, freq in sorted_words[:max_keywords]]
+            
+        except Exception as e:
+            print(f"关键词提取失败: {str(e)}")
+            return []
+    
+    def get_embedding_info(self) -> Dict[str, Any]:
+        """获取向量生成器信息"""
+        return {
+            "model": self.embedding_model,
+            "cache_size": len(self.cache),
+            "vector_dimension": 1536,  # OpenAI embedding维度
+            "provider": "openai"
+        }

+ 229 - 0
Co-creation-projects/Apricity-InnocoreAI/utils/pdf_parser.py

@@ -0,0 +1,229 @@
+"""
+PDF 解析工具
+支持从 PDF 文件中提取文本、标题、作者等信息
+"""
+
+import logging
+from typing import Dict, Any, Optional
+import re
+
+logger = logging.getLogger(__name__)
+
+class PDFParser:
+    """PDF 解析器"""
+    
+    def __init__(self):
+        """初始化 PDF 解析器"""
+        self.supported_formats = ['.pdf']
+    
+    async def parse_pdf(self, file_path: str) -> Dict[str, Any]:
+        """
+        解析 PDF 文件
+        
+        Args:
+            file_path: PDF 文件路径
+            
+        Returns:
+            包含解析结果的字典
+        """
+        try:
+            import pdfplumber
+            
+            logger.info(f"开始解析 PDF: {file_path}")
+            
+            with pdfplumber.open(file_path) as pdf:
+                # 提取所有文本
+                full_text = ""
+                for page in pdf.pages:
+                    text = page.extract_text()
+                    if text:
+                        full_text += text + "\n"
+                
+                if not full_text.strip():
+                    logger.warning("PDF 文件为空或无法提取文本")
+                    return {
+                        "success": False,
+                        "error": "无法从 PDF 中提取文本"
+                    }
+                
+                # 提取元数据
+                metadata = pdf.metadata or {}
+                
+                # 尝试从文本中提取标题(通常在第一页的前几行)
+                title = self._extract_title(full_text, metadata)
+                
+                # 尝试提取作者
+                authors = self._extract_authors(full_text, metadata)
+                
+                # 尝试提取摘要
+                abstract = self._extract_abstract(full_text)
+                
+                # 统计信息
+                page_count = len(pdf.pages)
+                word_count = len(full_text.split())
+                
+                result = {
+                    "success": True,
+                    "title": title,
+                    "authors": authors,
+                    "abstract": abstract,
+                    "full_text": full_text,
+                    "page_count": page_count,
+                    "word_count": word_count,
+                    "metadata": {
+                        "creator": metadata.get("/Creator", ""),
+                        "producer": metadata.get("/Producer", ""),
+                        "subject": metadata.get("/Subject", ""),
+                        "keywords": metadata.get("/Keywords", "")
+                    }
+                }
+                
+                logger.info(f"PDF 解析成功: {page_count} 页, {word_count} 词")
+                return result
+                
+        except ImportError:
+            logger.error("pdfplumber 未安装")
+            return {
+                "success": False,
+                "error": "PDF 解析库未安装,请运行: pip install pdfplumber"
+            }
+        except Exception as e:
+            logger.error(f"PDF 解析失败: {str(e)}")
+            return {
+                "success": False,
+                "error": f"PDF 解析失败: {str(e)}"
+            }
+    
+    def _extract_title(self, text: str, metadata: Dict) -> str:
+        """从文本或元数据中提取标题"""
+        # 首先尝试从元数据获取
+        if metadata.get("/Title"):
+            return metadata["/Title"]
+        
+        # 从文本前几行提取(通常标题在最前面且字体较大)
+        lines = text.split('\n')
+        for i, line in enumerate(lines[:10]):  # 只检查前10行
+            line = line.strip()
+            # 标题通常较长且不包含特殊字符
+            if len(line) > 10 and len(line) < 200 and not line.startswith(('http', 'www', '@')):
+                # 排除一些常见的非标题行
+                if not any(keyword in line.lower() for keyword in ['abstract', 'introduction', 'page', 'arxiv']):
+                    return line
+        
+        return "未知标题"
+    
+    def _extract_authors(self, text: str, metadata: Dict) -> list:
+        """从文本或元数据中提取作者"""
+        authors = []
+        
+        # 首先尝试从元数据获取
+        if metadata.get("/Author"):
+            author_str = metadata["/Author"]
+            authors = [a.strip() for a in re.split(r'[,;]', author_str) if a.strip()]
+            if authors:
+                return authors
+        
+        # 从文本中提取(通常在标题后面)
+        lines = text.split('\n')
+        for i, line in enumerate(lines[:20]):  # 检查前20行
+            line = line.strip()
+            # 查找包含作者信息的行(通常包含邮箱或机构)
+            if '@' in line or 'university' in line.lower() or 'institute' in line.lower():
+                # 尝试提取前面几行作为作者名
+                for j in range(max(0, i-3), i):
+                    potential_author = lines[j].strip()
+                    if potential_author and len(potential_author) < 100:
+                        # 简单的名字模式匹配
+                        if re.match(r'^[A-Z][a-z]+\s+[A-Z][a-z]+', potential_author):
+                            authors.append(potential_author)
+        
+        return authors if authors else ["未知作者"]
+    
+    def _extract_abstract(self, text: str) -> str:
+        """从文本中提取摘要"""
+        # 查找 Abstract 关键词
+        abstract_patterns = [
+            r'Abstract\s*[:\-]?\s*(.*?)(?=\n\n|\nIntroduction|\n1\.|\nKeywords)',
+            r'ABSTRACT\s*[:\-]?\s*(.*?)(?=\n\n|\nINTRODUCTION|\n1\.|\nKEYWORDS)',
+            r'摘要\s*[:\-]?\s*(.*?)(?=\n\n|关键词|引言|1\.)',
+        ]
+        
+        for pattern in abstract_patterns:
+            match = re.search(pattern, text, re.IGNORECASE | re.DOTALL)
+            if match:
+                abstract = match.group(1).strip()
+                # 限制摘要长度
+                if len(abstract) > 50 and len(abstract) < 2000:
+                    return abstract[:1000]  # 最多返回1000字符
+        
+        # 如果没找到,返回前500个字符作为摘要
+        return text[:500].strip() + "..."
+    
+    async def parse_pdf_from_bytes(self, pdf_bytes: bytes, filename: str = "document.pdf") -> Dict[str, Any]:
+        """
+        从字节流解析 PDF
+        
+        Args:
+            pdf_bytes: PDF 文件的字节内容
+            filename: 文件名(用于日志)
+            
+        Returns:
+            包含解析结果的字典
+        """
+        try:
+            import pdfplumber
+            import io
+            
+            logger.info(f"开始解析 PDF 字节流: {filename}")
+            
+            with pdfplumber.open(io.BytesIO(pdf_bytes)) as pdf:
+                # 提取所有文本
+                full_text = ""
+                for page in pdf.pages:
+                    text = page.extract_text()
+                    if text:
+                        full_text += text + "\n"
+                
+                if not full_text.strip():
+                    return {
+                        "success": False,
+                        "error": "无法从 PDF 中提取文本"
+                    }
+                
+                # 提取元数据
+                metadata = pdf.metadata or {}
+                
+                # 提取信息
+                title = self._extract_title(full_text, metadata)
+                authors = self._extract_authors(full_text, metadata)
+                abstract = self._extract_abstract(full_text)
+                
+                result = {
+                    "success": True,
+                    "title": title,
+                    "authors": authors,
+                    "abstract": abstract,
+                    "full_text": full_text,
+                    "page_count": len(pdf.pages),
+                    "word_count": len(full_text.split()),
+                    "metadata": {
+                        "creator": metadata.get("/Creator", ""),
+                        "producer": metadata.get("/Producer", ""),
+                        "subject": metadata.get("/Subject", ""),
+                        "keywords": metadata.get("/Keywords", "")
+                    }
+                }
+                
+                logger.info(f"PDF 字节流解析成功")
+                return result
+                
+        except Exception as e:
+            logger.error(f"PDF 字节流解析失败: {str(e)}")
+            return {
+                "success": False,
+                "error": f"PDF 解析失败: {str(e)}"
+            }
+
+
+# 全局 PDF 解析器实例
+pdf_parser = PDFParser()

+ 371 - 0
Co-creation-projects/Apricity-InnocoreAI/utils/text_processor.py

@@ -0,0 +1,371 @@
+"""
+InnoCore AI 文本处理工具
+"""
+
+import re
+from typing import List, Dict, Optional, Any, Tuple
+import string
+from collections import Counter
+import asyncio
+
+class TextProcessor:
+    """文本处理器"""
+    
+    def __init__(self):
+        self.stop_words = self._load_stop_words()
+        self.punctuation = string.punctuation
+    
+    def _load_stop_words(self) -> set:
+        """加载停用词"""
+        # 简化的停用词列表
+        return {
+            'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of',
+            'with', 'by', 'is', 'are', 'was', 'were', 'be', 'been', 'have', 'has',
+            'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may',
+            'might', 'must', 'can', 'this', 'that', 'these', 'those', 'i', 'you',
+            'he', 'she', 'it', 'we', 'they', 'me', 'him', 'her', 'us', 'them',
+            'my', 'your', 'his', 'her', 'its', 'our', 'their', 'mine', 'yours',
+            'hers', 'ours', 'theirs', 'what', 'which', 'who', 'whom', 'whose',
+            'where', 'when', 'why', 'how', 'all', 'each', 'every', 'both', 'few',
+            'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only',
+            'own', 'same', 'so', 'than', 'too', 'very', 'just', 'now', 'also'
+        }
+    
+    def clean_text(self, text: str) -> str:
+        """清理文本"""
+        if not text:
+            return ""
+        
+        # 移除多余的空白字符
+        text = re.sub(r'\s+', ' ', text)
+        
+        # 移除特殊字符(保留基本标点)
+        text = re.sub(r'[^\w\s\.\,\!\?\;\:\-\(\)\[\]\{\}\"\'\/\\]', ' ', text)
+        
+        # 移除多余的空格
+        text = re.sub(r'\s+', ' ', text).strip()
+        
+        return text
+    
+    def tokenize(self, text: str) -> List[str]:
+        """分词"""
+        if not text:
+            return []
+        
+        # 转换为小写并分词
+        words = text.lower().split()
+        
+        # 移除标点符号
+        words = [word.strip(self.punctuation) for word in words]
+        
+        # 过滤空字符串
+        words = [word for word in words if word]
+        
+        return words
+    
+    def remove_stop_words(self, words: List[str]) -> List[str]:
+        """移除停用词"""
+        return [word for word in words if word not in self.stop_words]
+    
+    def extract_sentences(self, text: str) -> List[str]:
+        """提取句子"""
+        if not text:
+            return []
+        
+        # 使用正则表达式分割句子
+        sentences = re.split(r'[.!?]+', text)
+        
+        # 清理和过滤
+        sentences = [s.strip() for s in sentences if s.strip()]
+        
+        return sentences
+    
+    def extract_paragraphs(self, text: str) -> List[str]:
+        """提取段落"""
+        if not text:
+            return []
+        
+        # 按双换行分割段落
+        paragraphs = re.split(r'\n\s*\n', text)
+        
+        # 清理和过滤
+        paragraphs = [p.strip() for p in paragraphs if p.strip()]
+        
+        return paragraphs
+    
+    def calculate_readability(self, text: str) -> Dict[str, float]:
+        """计算文本可读性指标"""
+        if not text:
+            return {"flesch_score": 0.0, "avg_sentence_length": 0.0, "avg_word_length": 0.0}
+        
+        sentences = self.extract_sentences(text)
+        words = self.tokenize(text)
+        
+        if not sentences or not words:
+            return {"flesch_score": 0.0, "avg_sentence_length": 0.0, "avg_word_length": 0.0}
+        
+        # 平均句子长度
+        avg_sentence_length = len(words) / len(sentences)
+        
+        # 平均词长
+        avg_word_length = sum(len(word) for word in words) / len(words)
+        
+        # 简化的Flesch Reading Ease分数
+        flesch_score = 206.835 - (1.015 * avg_sentence_length) - (84.6 * avg_word_length)
+        
+        return {
+            "flesch_score": max(0, min(100, flesch_score)),
+            "avg_sentence_length": avg_sentence_length,
+            "avg_word_length": avg_word_length
+        }
+    
+    def extract_key_phrases(self, text: str, max_phrases: int = 10) -> List[str]:
+        """提取关键短语"""
+        if not text:
+            return []
+        
+        # 简化的关键短语提取
+        words = self.tokenize(text)
+        words = self.remove_stop_words(words)
+        
+        # 寻找常见的学术短语模式
+        phrase_patterns = [
+            r'\b\w+\s+\w+\b',  # 两词短语
+            r'\b\w+\s+\w+\s+\w+\b',  # 三词短语
+        ]
+        
+        phrases = []
+        for pattern in phrase_patterns:
+            matches = re.findall(pattern, text.lower())
+            phrases.extend(matches)
+        
+        # 计算短语频率
+        phrase_freq = Counter(phrases)
+        
+        # 过滤和排序
+        filtered_phrases = [
+            phrase for phrase, freq in phrase_freq.items()
+            if freq > 1 and len(phrase.split()) >= 2
+        ]
+        
+        filtered_phrases.sort(key=lambda x: phrase_freq[x], reverse=True)
+        
+        return filtered_phrases[:max_phrases]
+    
+    def detect_language(self, text: str) -> str:
+        """检测语言(简化实现)"""
+        if not text:
+            return "unknown"
+        
+        # 简单的语言检测基于常见词汇
+        chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
+        english_chars = len(re.findall(r'[a-zA-Z]', text))
+        
+        total_chars = chinese_chars + english_chars
+        
+        if total_chars == 0:
+            return "unknown"
+        
+        chinese_ratio = chinese_chars / total_chars
+        
+        if chinese_ratio > 0.3:
+            return "chinese"
+        elif english_chars > 0:
+            return "english"
+        else:
+            return "unknown"
+    
+    def extract_citations(self, text: str) -> List[Dict[str, Any]]:
+        """提取引用"""
+        citations = []
+        
+        # 数字引用模式 [1], [2-3]
+        numeric_pattern = r'\[(\d+(?:-\d+)?)\]'
+        numeric_matches = re.finditer(numeric_pattern, text)
+        for match in numeric_matches:
+            citations.append({
+                "type": "numeric",
+                "text": match.group(0),
+                "reference": match.group(1),
+                "position": match.start()
+            })
+        
+        # 作者年份引用 (Smith, 2020)
+        author_year_pattern = r'\(([A-Za-z]+(?:\s+et\s+al\.)?,\s*\d{4})\)'
+        author_year_matches = re.finditer(author_year_pattern, text)
+        for match in author_year_matches:
+            citations.append({
+                "type": "author_year",
+                "text": match.group(0),
+                "reference": match.group(1),
+                "position": match.start()
+            })
+        
+        return citations
+    
+    def extract_numbers_and_units(self, text: str) -> List[Dict[str, Any]]:
+        """提取数字和单位"""
+        patterns = [
+            r'(\d+(?:\.\d+)?)\s*([a-zA-Z%]+)',  # 数字 + 单位
+            r'(\d+(?:,\d{3})*(?:\.\d+)?)',  # 带逗号的数字
+        ]
+        
+        results = []
+        for pattern in patterns:
+            matches = re.finditer(pattern, text)
+            for match in matches:
+                results.append({
+                    "text": match.group(0),
+                    "number": match.group(1),
+                    "unit": match.group(2) if len(match.groups()) > 1 else "",
+                    "position": match.start()
+                })
+        
+        return results
+    
+    def extract_acronyms(self, text: str) -> Dict[str, str]:
+        """提取缩写词"""
+        acronyms = {}
+        
+        # 查找全称(缩写)模式
+        acronym_pattern = r'([A-Za-z\s]+)\s*\(([A-Z]{2,})\)'
+        matches = re.finditer(acronym_pattern, text)
+        
+        for match in matches:
+            full_name = match.group(1).strip()
+            acronym = match.group(2)
+            
+            # 验证缩写是否来自全称的首字母
+            initials = ''.join([word[0].upper() for word in full_name.split() if word])
+            
+            if acronym.startswith(initials):
+                acronyms[acronym] = full_name
+        
+        return acronyms
+    
+    def summarize_text(self, text: str, max_sentences: int = 3) -> str:
+        """文本摘要(简化实现)"""
+        if not text:
+            return ""
+        
+        sentences = self.extract_sentences(text)
+        
+        if len(sentences) <= max_sentences:
+            return " ".join(sentences)
+        
+        # 简单的摘要算法:选择包含关键词最多的句子
+        words = self.tokenize(text)
+        words = self.remove_stop_words(words)
+        word_freq = Counter(words)
+        
+        sentence_scores = []
+        for sentence in sentences:
+            sentence_words = self.tokenize(sentence)
+            sentence_words = self.remove_stop_words(sentence_words)
+            
+            score = sum(word_freq.get(word, 0) for word in sentence_words)
+            sentence_scores.append((sentence, score))
+        
+        # 选择得分最高的句子
+        sentence_scores.sort(key=lambda x: x[1], reverse=True)
+        top_sentences = [sentence for sentence, score in sentence_scores[:max_sentences]]
+        
+        # 按原文顺序排列
+        summary_sentences = []
+        for sentence in sentences:
+            if sentence in top_sentences:
+                summary_sentences.append(sentence)
+        
+        return " ".join(summary_sentences)
+    
+    def extract_entities(self, text: str) -> Dict[str, List[str]]:
+        """实体提取(简化实现)"""
+        entities = {
+            "persons": [],
+            "organizations": [],
+            "locations": [],
+            "dates": [],
+            "numbers": []
+        }
+        
+        # 人名模式(简化)
+        person_pattern = r'\b([A-Z][a-z]+\s+[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*)\b'
+        person_matches = re.findall(person_pattern, text)
+        entities["persons"] = list(set(person_matches))
+        
+        # 组织模式(简化)
+        org_patterns = [
+            r'\b([A-Z][a-z]+\s+(?:University|Institute|Laboratory|Company|Corp|Inc|Ltd))\b',
+            r'\b((?:[A-Z]+\s*){2,})\b'
+        ]
+        for pattern in org_patterns:
+            matches = re.findall(pattern, text)
+            entities["organizations"].extend(matches)
+        entities["organizations"] = list(set(entities["organizations"]))
+        
+        # 日期模式
+        date_patterns = [
+            r'\b(\d{4})\b',
+            r'\b(\d{1,2}/\d{1,2}/\d{4})\b',
+            r'\b((?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{1,2},?\s+\d{4})\b'
+        ]
+        for pattern in date_patterns:
+            matches = re.findall(pattern, text)
+            entities["dates"].extend(matches)
+        entities["dates"] = list(set(entities["dates"]))
+        
+        # 数字模式
+        number_pattern = r'\b(\d+(?:\.\d+)?)\b'
+        number_matches = re.findall(number_pattern, text)
+        entities["numbers"] = list(set(number_matches))
+        
+        return entities
+    
+    def calculate_text_similarity(self, text1: str, text2: str) -> float:
+        """计算文本相似度(基于词汇重叠)"""
+        if not text1 or not text2:
+            return 0.0
+        
+        words1 = set(self.tokenize(text1))
+        words2 = set(self.tokenize(text2))
+        
+        if not words1 or not words2:
+            return 0.0
+        
+        intersection = words1.intersection(words2)
+        union = words1.union(words2)
+        
+        return len(intersection) / len(union)
+    
+    async def process_batch(self, texts: List[str], operations: List[str]) -> List[Dict[str, Any]]:
+        """批量处理文本"""
+        results = []
+        
+        for text in texts:
+            result = {"text": text}
+            
+            for operation in operations:
+                if operation == "clean":
+                    result["cleaned"] = self.clean_text(text)
+                elif operation == "tokenize":
+                    result["tokens"] = self.tokenize(text)
+                elif operation == "sentences":
+                    result["sentences"] = self.extract_sentences(text)
+                elif operation == "paragraphs":
+                    result["paragraphs"] = self.extract_paragraphs(text)
+                elif operation == "readability":
+                    result["readability"] = self.calculate_readability(text)
+                elif operation == "key_phrases":
+                    result["key_phrases"] = self.extract_key_phrases(text)
+                elif operation == "language":
+                    result["language"] = self.detect_language(text)
+                elif operation == "citations":
+                    result["citations"] = self.extract_citations(text)
+                elif operation == "entities":
+                    result["entities"] = self.extract_entities(text)
+                elif operation == "summary":
+                    result["summary"] = self.summarize_text(text)
+            
+            results.append(result)
+        
+        return results

+ 10 - 0
Co-creation-projects/allen2000-FashionDailyDress/.env.example

@@ -0,0 +1,10 @@
+# LLM配置
+LLM_API_KEY=xxxxx
+LLM_MODEL=Qwen/Qwen3-235B-A22B-Instruct-2507
+LLM_BASE_URL=https://api-inference.modelscope.cn/v1/
+
+# 天气API
+OPENWEATHER_API_KEY=
+
+# 搜索API
+SERPAPI_API_KEY=xxxxx

+ 161 - 0
Co-creation-projects/allen2000-FashionDailyDress/README.md

@@ -0,0 +1,161 @@
+# 🌤️ 多智能体天气穿衣建议系统
+
+一个基于多智能体协作的天气查询和穿衣建议系统,使用Python和Gradio构建。
+
+## 🎯 项目简介
+
+本项目实现了一个智能的天气穿衣建议系统,通过多个智能体协作处理用户查询:
+- **天气查询智能体**:负责获取实时天气数据
+- **穿衣建议智能体**:基于天气信息生成专业的穿衣建议
+- **协调器智能体**:管理智能体间的协作和任务分配
+
+## ✨ 核心功能
+
+- 🌤️ **实时天气查询**:支持全球主要城市的天气查询
+- 🤖 **AI智能建议**:基于天气数据生成专业的穿衣建议
+- 🔄 **多智能体协作**:智能体间高效协作处理复杂任务
+- 🌐 **Web界面**:友好的Gradio图形界面
+
+## 📁 项目结构
+
+```
+bill-FashionDailyDress/
+├── fashion_agent.py          # 穿衣建议智能体
+├── gradio_app.py            # Gradio Web界面
+├── multi_agent_coordinator.py # 多智能体协调器
+├── simple_multi_agent.py    # 简化版多智能体系统
+├── weather.py               # 天气查询功能
+├── weather_mcp.py           # MCP天气服务器
+├── requirements.txt         # 项目依赖
+└── README.md               # 项目说明文档
+```
+
+## 🚀 快速开始
+
+### 1. 环境准备
+
+```bash
+# 克隆项目
+git clone <repository-url>
+cd bill-FashionDailyDress
+
+# 安装依赖
+pip install -r requirements.txt
+```
+
+### 2. 环境变量配置
+
+创建 `.env` 文件并配置必要的API密钥:
+
+```env
+# LLM配置(必需)
+LLM_API_KEY=your_llm_api_key
+LLM_BASE_URL=your_llm_base_url
+LLM_MODEL_ID=your_llm_model_id
+
+# 天气API配置(可选,用于真实天气数据)
+OPENWEATHER_API_KEY=your_openweather_api_key
+```
+
+### 3. 运行系统
+
+#### 方式一:使用Gradio Web界面(推荐)
+python版本3.12.10
+```bash
+python gradio_app.py
+```
+访问 http://localhost:8899 使用图形界面
+
+#### 方式二:命令行交互
+```bash
+python simple_multi_agent.py
+```
+
+## 🔧 核心模块说明
+
+### fashion_agent.py
+- **功能**:专业的穿衣建议智能体
+- **特点**:基于温度、湿度、风速等天气因素提供详细建议
+- **输出**:包含服装搭配、配饰建议、注意事项等
+
+### multi_agent_coordinator.py
+- **功能**:多智能体协调器
+- **特点**:管理天气查询和穿衣建议智能体的协作
+- **流程**:接收查询 → 获取天气 → 生成建议 → 整合结果
+
+### gradio_app.py
+- **功能**:Web图形界面
+- **特点**:用户友好的交互界面,支持示例快速体验
+- **端口**:8899
+
+### weather.py
+- **功能**:天气查询封装
+- **特点**:支持真实API和演示模式
+- **API**:OpenWeatherMap API集成
+
+## 📋 使用示例
+
+### 输入示例
+- Beijing
+- Shanghai
+- Tokyo
+- London
+
+### 输出示例
+```
+🏙️ 查询城市: 北京
+🌡️ 温度: 25°C
+📝 天气: 晴朗
+💧 湿度: 60%
+🌬️ 风速: 3 m/s
+
+👗 穿衣建议:
+基于当前天气状况,建议穿着轻薄透气的衣物...
+```
+
+## ⚙️ 配置说明
+
+### 必需配置
+- LLM API密钥和端点(用于智能体推理)
+
+### 可选配置
+- OpenWeatherMap API密钥(用于真实天气数据)
+- 如不配置,系统将使用演示模式提供模拟数据
+
+## 🛠️ 技术栈
+
+- **框架**:hello-agents, fastmcp
+- **Web界面**:Gradio
+- **HTTP请求**:requests
+- **配置管理**:python-dotenv
+- **天气API**:OpenWeatherMap
+
+## 🔍 开发指南
+
+### 添加新的智能体
+1. 创建新的智能体类(参考fashion_agent.py)
+2. 在协调器中注册智能体
+3. 更新系统提示词和协作逻辑
+
+### 扩展功能
+- 支持更多天气数据源
+- 添加历史天气分析
+- 集成更多穿衣风格建议
+
+## 🤝 贡献指南
+
+欢迎提交Issue和Pull Request来改进项目!
+
+## 📄 许可证
+
+本项目采用MIT许可证。
+
+## 📞 联系方式
+
+如有问题或建议,请通过以下方式联系:
+- GitHub Issues
+- 项目维护者邮箱
+
+---
+
+**享受智能穿衣建议!** 👗✨

+ 134 - 0
Co-creation-projects/allen2000-FashionDailyDress/fashion_agent.py

@@ -0,0 +1,134 @@
+"""
+穿衣建议智能体
+专门处理基于天气信息的穿衣建议
+"""
+from hello_agents import SimpleAgent, HelloAgentsLLM
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+class FashionAgent:
+    """穿衣建议智能体"""
+    
+    def __init__(self, name="时尚顾问"):
+        """初始化穿衣建议智能体"""
+        self.name = name
+        self.agent = SimpleAgent(
+            name=name, 
+            llm=HelloAgentsLLM(
+                api_key=os.environ.get("LLM_API_KEY"),
+                base_url=os.environ.get("LLM_BASE_URL"),
+                model=os.environ.get("LLM_MODEL_ID")
+            )
+        )
+        
+        # 设置智能体的系统提示词
+        self._setup_prompt()
+    
+    def _setup_prompt(self):
+        """设置智能体的系统提示词"""
+        system_prompt = """你是一个专业的时尚顾问,专门根据天气信息提供穿衣建议。
+
+你的职责:
+1. 根据天气信息(温度、湿度、风速、天气状况)提供合适的穿衣建议
+2. 考虑不同季节、场合和人群的穿衣需求
+3. 提供具体的服装搭配建议,包括上衣、裤子、外套、鞋子等
+4. 考虑保暖、防晒、防雨等实际需求
+5. 给出时尚且实用的建议
+
+穿衣建议指南:
+- 高温天气(>30°C):建议轻薄透气的衣物,注意防晒
+- 温暖天气(20-30°C):建议舒适的单层衣物
+- 凉爽天气(10-20°C):建议长袖衣物,可搭配薄外套
+- 寒冷天气(<10°C):建议保暖衣物,如毛衣、羽绒服等
+- 雨天:建议防水外套和雨具
+- 大风天气:建议防风衣物
+
+请根据具体的天气信息提供详细、实用的穿衣建议。"""
+        
+        self.agent.system_prompt = system_prompt
+    
+    def get_fashion_advice(self, weather_info):
+        """
+        基于天气信息获取穿衣建议
+        
+        Args:
+            weather_info: 天气信息字符串或字典
+            
+        Returns:
+            穿衣建议字符串
+        """
+        # 构建查询提示
+        query = f"""请根据以下天气信息提供穿衣建议:
+
+天气信息:
+{weather_info}
+
+请提供详细的穿衣建议,包括:
+1. 适合的服装类型
+2. 具体的搭配建议
+3. 注意事项
+4. 时尚建议"""
+        
+        # 使用智能体获取建议
+        response = self.agent.run(query)
+        return response
+    
+    def get_detailed_fashion_advice(self, weather_data):
+        """
+        基于结构化天气数据获取更详细的穿衣建议
+        
+        Args:
+            weather_data: 包含天气信息的字典
+            
+        Returns:
+            详细的穿衣建议字符串
+        """
+        if isinstance(weather_data, dict):
+            # 从字典中提取关键信息
+            temperature = weather_data.get('temperature', '未知')
+            description = weather_data.get('description', '未知')
+            humidity = weather_data.get('humidity', '未知')
+            wind_speed = weather_data.get('wind_speed', '未知')
+            
+            query = f"""请根据以下详细的天气数据提供专业的穿衣建议:
+
+详细天气信息:
+- 温度: {temperature}°C
+- 天气状况: {description}
+- 湿度: {humidity}%
+- 风速: {wind_speed} m/s
+
+请提供:
+1. 适合的服装材质和类型
+2. 分层穿衣建议(适合不同温度变化)
+3. 配饰建议(帽子、围巾、手套等)
+4. 特殊天气条件下的注意事项
+5. 时尚搭配技巧"""
+        else:
+            query = f"请根据以下天气信息提供穿衣建议:\n\n{weather_data}"
+        
+        response = self.agent.run(query)
+        return response
+
+
+def main():
+    """测试函数"""
+    # 创建穿衣建议智能体
+    fashion_agent = FashionAgent()
+    
+    # 测试数据
+    test_weather = """🏙️ 城市: Shanghai
+🌡️ 温度: 25°C
+📝 天气: 晴朗
+💧 湿度: 60%
+🌬️ 风速: 3 m/s"""
+    
+    print("=== 穿衣建议智能体测试 ===")
+    advice = fashion_agent.get_fashion_advice(test_weather)
+    print(advice)
+
+
+if __name__ == "__main__":
+    main()

+ 228 - 0
Co-creation-projects/allen2000-FashionDailyDress/gradio_app.py

@@ -0,0 +1,228 @@
+"""
+Gradio前端界面 - 多智能体天气穿衣建议系统
+在本地8899端口提供服务
+"""
+import gradio as gr
+import sys
+import os
+
+# 添加当前目录到Python路径
+sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+
+def get_weather_and_fashion_advice(city_name):
+    """
+    获取指定城市的天气和穿衣建议
+    :param city_name: 城市名称
+    :return: 完整的响应文本
+    """
+    try:
+        # 导入多智能体协调器
+        from multi_agent_coordinator import MultiAgentCoordinator
+        
+        # 创建协调器实例
+        coordinator = MultiAgentCoordinator()
+        
+        # 构建查询
+        query = f"查询{city_name}的天气并给出穿衣建议"
+        
+        # 处理查询
+        result = coordinator.process_query(query)
+        
+        # 返回完整的响应文本
+        response_text = f"""
+🏙️ **查询城市**: {city_name}
+🔍 **查询内容**: {query}
+
+📊 **多智能体协作结果**:
+{result}
+
+💡 **系统说明**:
+- 本系统使用多智能体协作架构
+- 天气查询智能体负责获取天气数据
+- 穿衣建议智能体基于天气数据生成专业建议
+- 协调器负责智能体间的通信和任务分配
+"""
+        
+        return response_text
+        
+    except Exception as e:
+        # 如果多智能体系统不可用,使用简化版本
+        try:
+            from simple_multi_agent import get_real_weather, get_llm_fashion_advice
+            
+            # 获取天气信息
+            weather_info, weather_details = get_real_weather(city_name)
+            
+            # 获取LLM穿衣建议
+            fashion_advice = get_llm_fashion_advice(weather_details, city_name)
+            
+            response_text = f"""
+🏙️ **查询城市**: {city_name}
+
+📊 **天气信息**:
+{weather_info}
+
+🤖 **LLM智能穿衣建议**:
+{fashion_advice}
+
+💡 **系统说明**:
+- 使用简化版多智能体系统
+- 基于真实天气数据生成建议
+- LLM智能处理穿衣建议
+"""
+            return response_text
+            
+        except Exception as e2:
+            # 如果所有方法都失败,返回错误信息
+            error_text = f"""
+❌ **系统错误**: 无法获取{city_name}的天气和穿衣建议
+
+**错误信息**:
+- 主要错误: {str(e)}
+- 备用错误: {str(e2)}
+
+💡 **解决方案**:
+1. 检查网络连接
+2. 确保天气API服务可用
+3. 检查系统依赖是否完整安装
+
+🔧 **备用建议**:
+您可以尝试以下城市:北京、上海、广州、深圳、杭州、成都等
+"""
+            return error_text
+
+def create_gradio_interface():
+    """创建Gradio界面"""
+    
+    # 界面描述
+    description = """
+    # 🌤️ 多智能体天气穿衣建议系统
+    
+    输入您想查询的城市名称,系统将为您提供:
+    - 📊 实时天气信息
+    - 🤖 LLM智能穿衣建议
+    - 💡 专业穿搭指导
+    
+    **支持功能**:
+    - 多智能体协作处理
+    - 真实天气数据查询
+    - AI智能穿衣建议
+    - 多轮对话支持
+    """
+    
+    # 创建界面
+    with gr.Blocks(
+        title="多智能体天气穿衣建议系统",
+        theme=gr.themes.Soft(),
+        css="""
+        .gradio-container {
+            max-width: 900px !important;
+        }
+        .output-text {
+            font-size: 14px;
+            line-height: 1.6;
+        }
+        """
+    ) as demo:
+        
+        gr.Markdown(description)
+        
+        with gr.Row():
+            with gr.Column(scale=1):
+                city_input = gr.Textbox(
+                    label="🌍 请输入城市名称",
+                    placeholder="例如:北京、上海、广州、深圳...",
+                    info="支持中文和英文城市名"
+                )
+                
+                submit_btn = gr.Button(
+                    "🚀 获取穿衣建议",
+                    variant="primary",
+                    size="lg"
+                )
+                
+                clear_btn = gr.Button("🗑️ 清空", variant="secondary")
+                
+            with gr.Column(scale=2):
+                output_text = gr.Textbox(
+                    label="📋 完整响应结果",
+                    lines=20,
+                    max_lines=30,
+                    show_copy_button=True,
+                    elem_classes="output-text"
+                )
+        
+        # 示例城市
+        examples = gr.Examples(
+            examples=[["beijing"], ["tokoy"], ["london"], ["new york"], ["paris"], ["seoul"], ["bangkok"], ["harbin"]],
+            inputs=city_input,
+            label="💡 点击示例快速体验"
+        )
+        
+        # 按钮事件
+        submit_btn.click(
+            fn=get_weather_and_fashion_advice,
+            inputs=city_input,
+            outputs=output_text
+        )
+        
+        clear_btn.click(
+            fn=lambda: "",
+            inputs=[],
+            outputs=output_text
+        )
+        
+        # 回车键提交
+        city_input.submit(
+            fn=get_weather_and_fashion_advice,
+            inputs=city_input,
+            outputs=output_text
+        )
+        
+        # 页脚信息
+        gr.Markdown("""
+        ---
+        
+        ### 🔧 系统信息
+        - **版本**: v1.0.0
+        - **架构**: 多智能体协作系统
+        - **技术栈**: Python + Gradio + 多智能体框架
+        - **端口**: 8899
+        
+        ### 📖 使用说明
+        1. 在左侧输入框输入城市名称
+        2. 点击"获取穿衣建议"按钮或按回车键
+        3. 查看右侧的完整响应结果
+        4. 可以点击示例快速体验不同城市
+        
+        ### 🎯 功能特点
+        - 🌤️ 实时天气数据查询
+        - 🤖 AI智能穿衣建议
+        - 🔄 多智能体协作处理
+        - 📱 响应式Web界面
+        - 🎨 美观的用户体验
+        """)
+    
+    return demo
+
+def main():
+    """启动Gradio应用"""
+    print("🚀 启动多智能体天气穿衣建议系统 - Gradio前端")
+    print("🌐 服务地址: http://localhost:8899")
+    print("⏳ 正在启动服务...")
+    
+    # 创建界面
+    demo = create_gradio_interface()
+    
+    # 启动服务
+    demo.launch(
+        server_name="0.0.0.0",
+        server_port=8899,
+        share=False,
+        show_error=True,
+        debug=True,
+        quiet=False
+    )
+
+if __name__ == "__main__":
+    main()

+ 181 - 0
Co-creation-projects/allen2000-FashionDailyDress/multi_agent_coordinator.py

@@ -0,0 +1,181 @@
+"""
+多智能体协调器
+管理天气查询智能体和穿衣建议智能体的协作
+"""
+from hello_agents import SimpleAgent, HelloAgentsLLM
+from hello_agents.tools import MCPTool
+from fashion_agent import FashionAgent
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+class MultiAgentCoordinator:
+    """多智能体协调器"""
+    
+    def __init__(self):
+        """初始化协调器"""
+        # 创建主协调智能体
+        self.coordinator = SimpleAgent(
+            name="智能体协调器",
+            llm=HelloAgentsLLM(
+                api_key=os.environ.get("LLM_API_KEY"),
+                base_url=os.environ.get("LLM_BASE_URL"),
+                model=os.environ.get("LLM_MODEL_ID")
+            )
+        )
+        
+        # 创建天气查询智能体
+        self.weather_agent = self._create_weather_agent()
+        
+        # 创建穿衣建议智能体
+        self.fashion_agent = FashionAgent()
+        
+        # 设置协调器的系统提示词
+        self._setup_coordinator_prompt()
+    
+    def _create_weather_agent(self):
+        """创建天气查询智能体"""
+        weather_agent = SimpleAgent(
+            name="天气查询助手",
+            llm=HelloAgentsLLM(
+                api_key=os.environ.get("LLM_API_KEY"),
+                base_url=os.environ.get("LLM_BASE_URL"),
+                model=os.environ.get("LLM_MODEL_ID")
+            )
+        )
+        
+        # 配置MCP工具使用本地的weather_mcp.py服务器
+        mcp_tool = MCPTool(
+            name="query_weather",
+            server_command=["python", "weather_mcp.py"]
+        )
+        
+        weather_agent.add_tool(mcp_tool)
+        
+        # 设置天气智能体的系统提示词
+        weather_agent.system_prompt = """你是一个天气查询助手。你可以使用query_weather工具查询指定城市的天气信息。
+
+请根据用户提供的城市名称查询天气,并返回详细的天气信息。"""
+        
+        return weather_agent
+    
+    def _setup_coordinator_prompt(self):
+        """设置协调器的系统提示词"""
+        system_prompt = """你是一个智能体协调器,负责管理天气查询智能体和穿衣建议智能体的协作。
+
+你的工作流程:
+1. 接收用户关于天气和穿衣建议的查询
+2. 调用天气查询智能体获取指定城市的天气信息
+3. 将天气信息传递给穿衣建议智能体
+4. 整合两个智能体的结果,提供完整的天气和穿衣建议
+
+协作规则:
+- 首先获取准确的天气信息
+- 然后基于天气信息提供专业的穿衣建议
+- 确保信息的准确性和实用性
+- 提供清晰、完整的最终结果
+
+请按照这个流程处理用户的查询。"""
+        
+        self.coordinator.system_prompt = system_prompt
+    
+    def process_query(self, query):
+        """
+        处理用户查询,协调多个智能体完成任务
+        
+        Args:
+            query: 用户查询字符串
+            
+        Returns:
+            包含天气信息和穿衣建议的完整结果
+        """
+        print("=== 开始处理查询 ===")
+        print(f"用户查询: {query}")
+        print()
+        
+        # 步骤1: 使用天气智能体查询天气
+        print("步骤1: 查询天气信息...")
+        weather_response = self.weather_agent.run(query)
+        print(f"天气查询结果: {weather_response}")
+        print()
+        
+        # 步骤2: 使用穿衣建议智能体提供建议
+        print("步骤2: 生成穿衣建议...")
+        fashion_advice = self.fashion_agent.get_fashion_advice(weather_response)
+        print(f"穿衣建议: {fashion_advice}")
+        print()
+        
+        # 步骤3: 整合结果
+        print("步骤3: 整合最终结果...")
+        final_result = self._format_final_result(weather_response, fashion_advice)
+        
+        return final_result
+    
+    def _format_final_result(self, weather_info, fashion_advice):
+        """
+        格式化最终结果
+        
+        Args:
+            weather_info: 天气信息
+            fashion_advice: 穿衣建议
+            
+        Returns:
+            格式化的完整结果
+        """
+        result = f"""🎯 智能体协作完成!以下是您的完整天气和穿衣建议:
+
+🌤️ 天气信息:
+{weather_info}
+
+👗 穿衣建议:
+{fashion_advice}
+
+💡 温馨提示:
+- 请根据实际体感温度调整穿着
+- 考虑当天的具体活动安排
+- 如有特殊需求,可进一步咨询"""
+        
+        return result
+    
+    def get_weather_only(self, city_name):
+        """
+        仅获取天气信息(不包含穿衣建议)
+        
+        Args:
+            city_name: 城市名称
+            
+        Returns:
+            天气信息
+        """
+        query = f"查询{city_name}的天气"
+        return self.weather_agent.run(query)
+    
+    def get_fashion_advice_only(self, weather_info):
+        """
+        基于现有天气信息获取穿衣建议
+        
+        Args:
+            weather_info: 天气信息字符串
+            
+        Returns:
+            穿衣建议
+        """
+        return self.fashion_agent.get_fashion_advice(weather_info)
+
+
+def main():
+    """测试函数"""
+    # 创建多智能体协调器
+    coordinator = MultiAgentCoordinator()
+    
+    # 测试查询
+    test_query = "查询上海的天气并给出穿衣建议"
+    
+    print("=== 多智能体协调器测试 ===")
+    result = coordinator.process_query(test_query)
+    print(result)
+
+
+if __name__ == "__main__":
+    main()

+ 12 - 0
Co-creation-projects/allen2000-FashionDailyDress/requirements.txt

@@ -0,0 +1,12 @@
+# 多智能体天气穿衣建议系统依赖
+
+# 核心框架
+hello-agents[all]==0.2.8
+fastmcp==2.13.1
+
+# Web界面
+requests==2.32.5
+gradio==4.44.1
+
+# 环境变量管理
+dotenv==0.9.9

+ 257 - 0
Co-creation-projects/allen2000-FashionDailyDress/simple_multi_agent.py

@@ -0,0 +1,257 @@
+"""
+简单多智能体天气穿衣建议系统
+提供直接的城市输入功能,使用真实天气数据,穿衣建议由LLM处理
+"""
+import sys
+import os
+import json
+import random
+
+def get_city_input():
+    """获取用户输入的城市名称"""
+    print("💬 欢迎使用多智能体天气穿衣建议系统!")
+    print("💡 请输入您想查询天气的城市名称")
+    print("💡 支持中文和英文城市名")
+    print("💡 输入 'quit' 或 '退出' 可以退出程序\n")
+    
+    while True:
+        city = input("🌍 请输入城市名称: ").strip()
+        if not city:
+            print("❌ 请输入有效的城市名称")
+            continue
+            
+        return city
+
+def get_real_weather(city_name):
+    """获取真实天气数据"""
+    try:
+        # 导入Weather类
+        from weather import Weather
+        
+        # 创建天气查询实例
+        weather = Weather()
+        
+        # 查询天气信息
+        weather_info = weather.get_weather(city_name)
+        
+        # 获取详细天气数据用于穿衣建议
+        weather_details = weather.get_weather_details(city_name)
+        
+        return weather_info, weather_details
+        
+    except Exception as e:
+        print(f"❌ 天气查询失败: {e}")
+        # 返回模拟数据作为备用
+        return f"🏙️ 城市: {city_name}\n🌡️ 温度: 20°C\n📝 天气: 晴朗\n💧 湿度: 50%\n🌬️ 风速: 3 m/s", None
+
+def get_llm_fashion_advice(weather_details, city_name):
+    """使用LLM基于真实天气数据生成穿衣建议"""
+    if weather_details and "error" not in weather_details:
+        # 使用真实天气数据
+        temp = weather_details.get('temperature', 20)
+        description = weather_details.get('description', '晴朗')
+        humidity = weather_details.get('humidity', 50)
+        wind_speed = weather_details.get('wind_speed', 3)
+        
+        # 构建LLM提示词
+        prompt = f"""
+请根据以下天气信息为{city_name}提供专业的穿衣建议:
+- 温度: {temp}°C
+- 天气状况: {description}
+- 湿度: {humidity}%
+- 风速: {wind_speed} m/s
+
+请提供详细、实用的穿衣建议,包括:
+1. 上衣选择
+2. 下装选择  
+3. 鞋子建议
+4. 外套/配饰
+5. 特别注意事项
+
+请用中文回复,格式要清晰易读。"""
+        
+        # 模拟LLM响应(在实际应用中,这里会调用真实的LLM API)
+        llm_response = simulate_llm_response(prompt, temp, description)
+        
+        return f"🤖 LLM智能穿衣建议(基于{city_name}的真实天气数据):\n\n{llm_response}"
+    else:
+        # 使用模拟数据
+        prompt = f"""
+请根据以下天气信息为{city_name}提供专业的穿衣建议:
+- 温度: 20°C
+- 天气状况: 晴朗
+- 湿度: 50%
+- 风速: 3 m/s
+
+请提供详细、实用的穿衣建议。"""
+        
+        llm_response = simulate_llm_response(prompt, 20, '晴朗')
+        return f"🤖 LLM智能穿衣建议(基于{city_name}的天气数据):\n\n{llm_response}"
+
+def simulate_llm_response(prompt, temperature, weather_condition):
+    """模拟LLM响应,根据温度生成智能穿衣建议"""
+    
+    # 基于温度生成不同的建议模板
+    if temperature > 30:
+        base_advice = {
+            "上衣": ["短袖T恤", "透气衬衫", "背心", "轻薄材质上衣"],
+            "下装": ["短裤", "轻薄长裤", "透气材质的裤子"],
+            "鞋子": ["凉鞋", "透气运动鞋", "帆布鞋"],
+            "外套": ["防晒衣", "轻薄外套备用"],
+            "配饰": ["太阳镜", "遮阳帽", "防晒霜"],
+            "建议": "选择浅色、透气的面料,注意防晒和补水"
+        }
+    elif temperature > 25:
+        base_advice = {
+            "上衣": ["短袖T恤", "轻薄长袖", "透气衬衫"],
+            "下装": ["休闲裤", "牛仔裤", "轻薄材质的裤子"],
+            "鞋子": ["运动鞋", "休闲鞋", "帆布鞋"],
+            "外套": ["薄外套备用", "防风衣"],
+            "配饰": ["帽子", "太阳镜"],
+            "建议": "可分层穿着,便于根据温度变化调整"
+        }
+    elif temperature > 20:
+        base_advice = {
+            "上衣": ["长袖T恤", "薄款卫衣", "衬衫"],
+            "下装": ["休闲裤", "牛仔裤", "卡其裤"],
+            "鞋子": ["休闲鞋", "运动鞋", "皮鞋"],
+            "外套": ["夹克", "薄外套", "风衣"],
+            "配饰": ["围巾备用", "帽子"],
+            "建议": "温度适宜,适合户外活动"
+        }
+    elif temperature > 15:
+        base_advice = {
+            "上衣": ["长袖衬衫", "薄毛衣", "卫衣"],
+            "下装": ["长裤", "牛仔裤", "休闲裤"],
+            "鞋子": ["运动鞋", "皮鞋", "休闲鞋"],
+            "外套": ["夹克", "风衣", "薄外套"],
+            "配饰": ["围巾", "帽子"],
+            "建议": "注意保暖,可搭配薄外套"
+        }
+    elif temperature > 10:
+        base_advice = {
+            "上衣": ["毛衣", "厚衬衫", "保暖内衣"],
+            "下装": ["厚裤子", "牛仔裤", "保暖裤"],
+            "鞋子": ["运动鞋", "皮鞋", "保暖鞋"],
+            "外套": ["夹克", "风衣", "薄羽绒服"],
+            "配饰": ["围巾", "手套", "帽子"],
+            "建议": "注意保暖,可搭配围巾"
+        }
+    else:
+        base_advice = {
+            "上衣": ["保暖内衣", "厚毛衣", "羽绒内胆"],
+            "下装": ["厚裤子", "保暖裤", "羽绒裤"],
+            "鞋子": ["保暖靴子", "雪地靴", "防水鞋"],
+            "外套": ["羽绒服", "厚外套", "防风衣"],
+            "配饰": ["围巾", "手套", "帽子", "耳罩"],
+            "建议": "多层穿着,注意防寒保暖"
+        }
+    
+    # 根据天气状况调整建议
+    weather_adjustments = {
+        "雨": {
+            "鞋子": ["雨靴", "防水鞋"],
+            "配饰": ["雨伞", "雨衣"],
+            "建议": "选择防水面料,注意防滑"
+        },
+        "雪": {
+            "鞋子": ["防滑靴", "雪地靴"],
+            "配饰": ["手套", "帽子", "围巾"],
+            "建议": "注意防滑保暖,选择防水材质"
+        },
+        "风": {
+            "外套": ["防风衣", "风衣"],
+            "配饰": ["帽子", "围巾"],
+            "建议": "选择防风面料,注意保暖"
+        }
+    }
+    
+    # 应用天气调整
+    for weather_key, adjustment in weather_adjustments.items():
+        if weather_key in weather_condition:
+            for category, items in adjustment.items():
+                if category in base_advice:
+                    if category == "建议":
+                        base_advice[category] += f",{items}"
+                    else:
+                        base_advice[category].extend(items)
+    
+    # 构建响应文本
+    response = f"基于当前天气状况({temperature}°C,{weather_condition}),为您提供以下穿衣建议:\n\n"
+    
+    response += f"👕 **上衣选择**: {', '.join(base_advice['上衣'][:3])}\n"
+    response += f"👖 **下装选择**: {', '.join(base_advice['下装'][:3])}\n"
+    response += f"👟 **鞋子建议**: {', '.join(base_advice['鞋子'][:3])}\n"
+    response += f"🧥 **外套配饰**: {', '.join(base_advice['外套'][:2])}"
+    
+    if base_advice['配饰']:
+        response += f",配饰: {', '.join(base_advice['配饰'][:3])}\n"
+    else:
+        response += "\n"
+    
+    response += f"💡 **特别建议**: {base_advice['建议']}\n"
+    
+    # 添加个性化建议
+    if temperature > 25:
+        response += "🌞 **温馨提示**: 天气较热,建议选择透气材质,注意防晒补水"
+    elif temperature < 10:
+        response += "❄️ **温馨提示**: 天气较冷,建议多层穿着,注意保暖防寒"
+    else:
+        response += "🌤️ **温馨提示**: 天气舒适,适合各种户外活动"
+    
+    return response
+
+def main():
+    """主函数"""
+    try:
+        # 获取城市输入
+        city = get_city_input()
+        
+        print(f"\n🔍 正在查询 {city} 的真实天气信息...")
+        
+        # 获取真实天气数据
+        weather_info, weather_details = get_real_weather(city)
+        
+        # 生成穿衣建议(使用LLM处理)
+        print("🤖 正在使用LLM生成智能穿衣建议...")
+        fashion_advice = get_llm_fashion_advice(weather_details, city)
+        
+        # 显示结果
+        print("\n" + "="*50)
+        print("📊 天气信息")
+        print("="*50)
+        print(weather_info)
+        
+        print("\n" + "="*50)
+        print("👗 穿衣建议")
+        print("="*50)
+        print(fashion_advice)
+        
+        print("\n" + "="*50)
+        
+        # 询问是否继续查询
+        while True:
+            continue_query = input("\n🔍 是否继续查询其他城市?(y/n): ").strip().lower()
+            
+            if continue_query in ['y', 'yes', '是', '继续']:
+                print("\n" + "-"*50)
+                # 递归调用主函数继续查询
+                main()
+                return
+            elif continue_query in ['n', 'no', '否', '退出']:
+                print("👋 感谢使用,再见!")
+                return
+            else:
+                print("❌ 请输入 y/是 或 n/否")
+                
+    except KeyboardInterrupt:
+        print("\n\n👋 用户中断,程序退出")
+        sys.exit(0)
+    except Exception as e:
+        print(f"\n❌ 发生错误: {e}")
+        print("💡 请稍后重试")
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main()

+ 163 - 0
Co-creation-projects/allen2000-FashionDailyDress/weather.py

@@ -0,0 +1,163 @@
+import requests
+import os
+from dotenv import load_dotenv
+
+load_dotenv()
+
+class Weather:
+    """天气查询类,封装OpenWeatherMap API功能"""
+    
+    def __init__(self, api_key=None, unit='metric'):
+        """
+        初始化Weather类
+        :param api_key: OpenWeatherMap API密钥,默认为环境变量中的OPENWEATHER_API_KEY
+        :param unit: 温度单位(metric=摄氏,imperial=华氏)
+        """
+        self.api_key = api_key or os.environ.get("OPENWEATHER_API_KEY")
+        self.unit = unit
+        self.base_url = "http://api.openweathermap.org/data/2.5/weather"
+
+        # 如果没有API密钥,使用模拟数据
+        self.demo_mode = not self.api_key
+        if self.demo_mode:
+            print("⚠️  警告: 未设置API密钥,使用演示模式")
+            print("   请设置OPENWEATHER_API_KEY环境变量以获得真实天气数据")
+    
+    def get_weather(self, city_name):
+        """
+        查询指定城市的天气信息
+        :param city_name: 城市名称(英文)
+        :return: 格式化后的天气信息字符串
+        """
+        # 如果是演示模式,使用模拟数据
+        if self.demo_mode:
+            return self._get_demo_weather()
+        
+        params = {
+            "q": city_name,
+            "appid": self.api_key,
+            "units": self.unit
+        }
+
+        try:
+            response = requests.get(self.base_url, params=params)
+            data = response.json()
+
+            if response.status_code == 200:
+                return self._format_weather_data(data)
+            else:
+                return f"错误 {data['cod']}: {data['message']}"
+
+        except Exception as e:
+            return f"请求失败: {str(e)}"
+    
+    def get_weather_details(self, city_name):
+        """
+        获取详细的天气数据(字典格式)
+        :param city_name: 城市名称(英文)
+        :return: 包含详细天气数据的字典
+        """
+        # 如果是演示模式,使用模拟数据
+        if self.demo_mode:
+            return self._get_demo_weather()
+        
+        params = {
+            "q": city_name,
+            "appid": self.api_key,
+            "units": self.unit
+        }
+
+        try:
+            response = requests.get(self.base_url, params=params)
+            data = response.json()
+
+            if response.status_code == 200:
+                return self._parse_weather_data(data)
+            else:
+                return {"error": f"错误 {data['cod']}: {data['message']}"}
+
+        except Exception as e:
+            return {"error": f"请求失败: {str(e)}"}
+
+    def _get_demo_weather(self):
+        return {
+            "city": 'shanghai',
+            "temperature": 25,
+            "temperature_unit": "°C",
+            "description": "晴天",
+            "humidity": 60,
+            "wind_speed": 10,
+            "wind_unit": "m/s"
+        }
+    def _parse_weather_data(self, data):
+        """
+        解析天气数据为字典格式
+        :param data: API返回的原始数据
+        :return: 解析后的天气数据字典
+        """
+        weather_desc = data['weather'][0]['description'].title()
+        temp = data['main']['temp']
+        humidity = data['main']['humidity']
+        wind_speed = data['wind']['speed']
+        city = data['name']
+        
+        return {
+            "city": city,
+            "temperature": temp,
+            "temperature_unit": "°C" if self.unit == 'metric' else "°F",
+            "description": weather_desc,
+            "humidity": humidity,
+            "wind_speed": wind_speed,
+            "wind_unit": "m/s"
+        }
+    
+    def _format_weather_data(self, data):
+        """
+        格式化天气数据为字符串
+        :param data: API返回的原始数据
+        :return: 格式化后的天气信息字符串
+        """
+        weather_data = self._parse_weather_data(data)
+        
+        return (
+            f"🏙️ 城市: {weather_data['city']}\n"
+            f"🌡️ 温度: {weather_data['temperature']}{weather_data['temperature_unit']}\n"
+            f"📝 天气: {weather_data['description']}\n"
+            f"💧 湿度: {weather_data['humidity']}%\n"
+            f"🌬️ 风速: {weather_data['wind_speed']} {weather_data['wind_unit']}"
+        )
+    
+    def set_unit(self, unit):
+        """
+        设置温度单位
+        :param unit: 温度单位(metric=摄氏,imperial=华氏)
+        """
+        if unit not in ['metric', 'imperial']:
+            raise ValueError("单位必须是 'metric' 或 'imperial'")
+        self.unit = unit
+    
+    def set_api_key(self, api_key):
+        """
+        设置API密钥
+        :param api_key: 新的API密钥
+        """
+        self.api_key = api_key
+
+
+def get_weather(city_name, api_key=os.environ.get("OPENWEATHER_API_KEY"), unit='metric'):
+    """
+    向后兼容的函数,使用Weather类实现
+    :param city_name: 城市名称(英文)
+    :param api_key: 你的OpenWeatherMap API密钥
+    :param unit: 温度单位(metric=摄氏,imperial=华氏)
+    :return: 格式化后的天气信息
+    """
+    weather = Weather(api_key=api_key, unit=unit)
+    return weather.get_weather(city_name)
+
+
+# 使用示例
+if __name__ == "__main__":
+    weather = Weather()
+    weather_info = weather.get_weather("harbin")
+    print(weather_info)

+ 97 - 0
Co-creation-projects/allen2000-FashionDailyDress/weather_mcp.py

@@ -0,0 +1,97 @@
+"""
+自定义MCP服务器示例
+
+这是一个简单的MCP服务器,提供天气信息查询。
+用于演示如何创建自己的MCP服务器。
+
+运行方式:
+    python my_mcp_server.py
+
+或者作为MCP服务器被客户端调用:
+    MCPClient(["python", "weather_mcp.py"])
+"""
+from fastmcp import FastMCP
+from weather import Weather
+# 创建MCP服务器实例
+mcp = FastMCP("WeatherServer")
+
+
+# ==================== 数学工具 ====================
+
+@mcp.tool()
+def query_wearher(city_name: str):
+    """
+    查询天气
+
+    Args:
+        city_name: 城市名称
+
+    Returns:
+        天气信息
+    """
+    weather = Weather()
+    # 查询天气详细信息(字典格式)
+    weather_details = weather.get_weather_details(city_name)
+    
+    # 如果查询成功,返回详细信息
+    if "error" not in weather_details:
+        return weather_details
+    else:
+        # 如果查询失败,返回格式化字符串
+        return weather.get_weather(city_name)
+
+@mcp.tool()
+def get_weather_details(city_name: str):
+    """
+    获取详细的天气数据(结构化格式)
+
+    Args:
+        city_name: 城市名称
+
+    Returns:
+        包含详细天气数据的字典
+    """
+    weather = Weather()
+    return weather.get_weather_details(city_name)
+
+@mcp.resource("info://capabilities")
+def get_capabilities() -> str:
+    """
+    获取指定城市的天气信息
+
+    Returns:
+        能力列表的文本描述
+    """
+    capabilities = """
+服务器能力列表:
+
+天气查询能力:
+- query_weather: 获取指定城市的天气信息(结构化数据)
+- get_weather_details: 获取详细的天气数据(字典格式)
+"""
+    return capabilities.strip()
+
+
+# ==================== 提示词模板 ====================
+
+@mcp.prompt()
+def weather_helper() -> str:
+    """
+    天气信息查询提示词
+
+    Returns:
+        提示词模板
+    """
+    return """你是一个天气查询助手。你可以使用以下工具:
+- query_weather(city_name): 获取指定城市的天气信息
+
+请根据用户的问题选择合适的工具进行任务执行。"""
+
+
+
+# ==================== 主程序 ====================
+
+if __name__ == "__main__":
+    # 运行MCP服务器
+    # FastMCP会自动处理stdio传输
+    mcp.run()

+ 14 - 0
Co-creation-projects/chen070808-ProgrammingTutor/.env.example

@@ -0,0 +1,14 @@
+# HelloAgents LLM配置
+
+# 模型名称
+LLM_MODEL_ID=Qwen/Qwen2.5-72B-Instruct
+
+# API密钥
+LLM_API_KEY=your_api_key_here
+
+# 服务地址
+LLM_BASE_URL=https://api-inference.modelscope.cn/v1
+
+# 超时时间(可选,默认60秒)
+LLM_TIMEOUT=60
+

+ 201 - 0
Co-creation-projects/chen070808-ProgrammingTutor/README.md

@@ -0,0 +1,201 @@
+# 智能编程导师 (Intelligent Programming Tutor)
+
+一个基于 HelloAgents 框架的智能编程学习助手系统,提供个性化的编程学习体验。
+
+## 功能特点
+
+### 🎯 核心功能
+
+- **学习路径规划**:根据学习目标和当前水平,制定个性化的学习计划
+- **智能出题**:根据学习内容自动生成编程练习题
+- **代码评审**:对提交的代码进行专业评审,提供改进建议和最佳实践指导
+
+### 🤖 多智能体架构
+
+本项目采用多智能体协同工作模式,包含以下智能体:
+
+- **TutorAgent(导师)**:主协调智能体,负责理解用户需求并调用相应的子智能体
+- **PlannerAgent(规划师)**:制定个性化学习计划和路径
+- **ExerciseAgent(出题人)**:根据学习内容生成编程练习题
+- **ReviewerAgent(评审员)**:评审代码并提供专业反馈,支持代码执行测试
+
+## 项目结构
+
+```
+chen070808-ProgrammingTutor/
+├── src/
+│   ├── agents/          # 智能体定义
+│   │   ├── tutor.py     # 主导师智能体
+│   │   ├── planner.py   # 学习规划智能体
+│   │   ├── exercise.py  # 出题智能体
+│   │   └── reviewer.py  # 代码评审智能体
+│   └── tools/           # 工具定义
+│       ├── agent_tool.py    # A2A智能体工具包装
+│       └── code_runner.py   # 代码执行工具
+├── main.ipynb           # 示例演示 Notebook
+├── requirements.txt     # 项目依赖
+└── .env                 # 环境配置(需自行创建)
+```
+
+## 安装与配置
+
+### 1. 环境要求
+
+- Python 3.8+
+- HelloAgents 框架
+
+### 2. 安装依赖
+
+```bash
+pip install -r requirements.txt
+```
+
+### 3. 配置环境变量
+
+创建 `.env` 文件并配置以下参数:
+
+```bash
+# LLM 模型配置
+LLM_MODEL_ID=Qwen/Qwen2.5-72B-Instruct
+LLM_API_KEY=your_api_key_here
+LLM_BASE_URL=https://api-inference.modelscope.cn/v1
+LLM_TIMEOUT=60
+```
+
+参考 `.env.example` 文件了解完整配置选项。
+
+## 使用方法
+
+### 方式一:Jupyter Notebook
+
+打开 `main.ipynb` 并按顺序运行各个单元格,体验完整的学习流程:
+
+1. 学习路径规划
+2. 获取编程练习题
+3. 代码评审与反馈
+
+### 方式二:Python 代码
+
+```python
+from hello_agents import HelloAgentsLLM
+from src.agents.tutor import TutorAgent
+
+# 初始化 LLM
+llm = HelloAgentsLLM.from_env()
+
+# 创建导师智能体
+tutor = TutorAgent(llm)
+
+# 示例 1:请求学习计划
+response = tutor.run("我想学习 Python 中的列表推导式")
+print(response)
+
+# 示例 2:请求练习题
+response = tutor.run("请给我出一道关于列表推导式的练习题")
+print(response)
+
+# 示例 3:代码评审
+code = """
+numbers = [1, 2, 3, 4, 5]
+squares = []
+for n in numbers:
+    squares.append(n * n)
+"""
+response = tutor.run(f"请评审以下代码:{code}")
+print(response)
+```
+
+## 技术架构
+
+### 智能体协同机制
+
+- 采用 **Agent-to-Agent (A2A)** 工具调用模式
+- `TutorAgent` 通过工具接口调用子智能体:
+  - `call_planner(query)` - 调用学习规划师
+  - `call_exercise(query)` - 调用出题人
+  - `call_reviewer(query)` - 调用代码评审员
+
+### 代码执行能力
+
+`ReviewerAgent` 集成了 `CodeRunner` 工具,可以:
+- 安全执行用户提交的 Python 代码
+- 捕获运行时错误和异常
+- 基于执行结果提供更精准的反馈
+
+## 示例场景
+
+### 场景 1:学习路径规划
+
+**用户输入**:
+```
+我想学习 Python 中的装饰器,但我只了解基础的函数定义
+```
+
+**系统响应**:
+导师会调用 PlannerAgent,生成包含以下内容的学习计划:
+- 前置知识检查
+- 分阶段学习目标
+- 推荐学习资源
+- 实践项目建议
+
+### 场景 2:获取练习题
+
+**用户输入**:
+```
+请给我出一道关于装饰器的练习题
+```
+
+**系统响应**:
+ExerciseAgent 会生成一道练习题,包含:
+- 题目描述和要求
+- 输入输出示例
+- 难度级别
+- 考察知识点
+
+### 场景 3:代码评审
+
+**用户输入**:
+```python
+@decorator
+def greet(name):
+    print(f"Hello, {name}")
+```
+
+**系统响应**:
+ReviewerAgent 会:
+1. 执行代码检查语法和运行时错误
+2. 分析代码质量和最佳实践
+3. 提供改进建议
+4. 指出潜在问题
+
+## 开发与扩展
+
+### 添加新的智能体
+
+1. 在 `src/agents/` 目录下创建新的智能体类
+2. 继承 `SimpleAgent` 基类
+3. 在 `TutorAgent` 中注册新智能体工具
+
+### 自定义工具
+
+1. 在 `src/tools/` 目录下创建新工具类
+2. 继承 `Tool` 基类并实现 `run()` 方法
+3. 将工具注入到相应的智能体中
+
+## 注意事项
+
+- 确保 `.env` 文件配置正确,特别是 API 密钥
+- 代码执行功能默认启用沙箱模式,建议不要执行不可信代码
+- LLM 调用需要网络连接,请确保网络畅通
+
+## 贡献者
+
+- chen070808
+
+## 许可证
+
+本项目遵循 MIT 许可证。
+
+## 致谢
+
+本项目基于 [HelloAgents](https://github.com/datawhalechina/hello-agents) 框架开发。

+ 534 - 0
Co-creation-projects/chen070808-ProgrammingTutor/main.ipynb

@@ -0,0 +1,534 @@
+{
+    "cells": [
+        {
+            "cell_type": "markdown",
+            "id": "252eb5a2",
+            "metadata": {},
+            "source": [
+                "# 智能编程导师 (Intelligent Programming Tutor)\n",
+                "\n",
+                "一个基于多智能体协作的个性化编程学习系统,展示了如何使用 `hello-agents` 框架构建复杂的 Agent-to-Agent (A2A) 协作系统。\n",
+                "\n",
+                "## 系统架构\n",
+                "\n",
+                "本系统采用分层智能体架构:\n",
+                "\n",
+                "- **Tutor(导师)**:主协调智能体,负责与用户交互并调度子智能体\n",
+                "- **Planner(规划师)**:分析用户需求,制定个性化学习计划\n",
+                "- **Exercise(出题人)**:根据学习内容生成针对性的编程练习题\n",
+                "- **Reviewer(评审员)**:评审用户代码,提供专业反馈和改进建议\n",
+                "\n",
+                "## 技术特点\n",
+                "\n",
+                "1. **多智能体协作**:使用 `AgentTool` 将子智能体封装为工具,实现 A2A 通信\n",
+                "2. **工具调用**:Reviewer 配备 `CodeRunner` 工具,可执行 Python 代码验证\n",
+                "3. **模块化设计**:每个智能体职责单一,易于维护和扩展\n",
+                "\n",
+                "## 演示说明\n",
+                "\n",
+                "本 notebook 包含三个完整的测试场景,展示了智能编程导师的核心功能。"
+            ]
+        },
+        {
+            "cell_type": "markdown",
+            "id": "setup_intro",
+            "metadata": {},
+            "source": [
+                "## 步骤 1:环境设置\n",
+                "\n",
+                "初始化 LLM 和环境配置。\n",
+                "\n",
+                "**关键组件**:\n",
+                "- `HelloAgentsLLM`:统一的 LLM 接口\n",
+                "- `.env` 文件:存储 API 密钥等敏感信息\n",
+                "- `src` 路径:包含自定义智能体和工具的实现"
+            ]
+        },
+        {
+            "cell_type": "code",
+            "execution_count": 1,
+            "id": "3a142c96",
+            "metadata": {},
+            "outputs": [
+                {
+                    "name": "stdout",
+                    "output_type": "stream",
+                    "text": [
+                        "✅ 环境配置完成\n",
+                        "✅ LLM 已初始化\n"
+                    ]
+                },
+                {
+                    "name": "stderr",
+                    "output_type": "stream",
+                    "text": [
+                        "/Users/chen/vs_code/hello_agent/hello-agents/.conda/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
+                        "  from .autonotebook import tqdm as notebook_tqdm\n"
+                    ]
+                }
+            ],
+            "source": [
+                "# 1. 环境设置\n",
+                "import os\n",
+                "import sys\n",
+                "from dotenv import load_dotenv\n",
+                "from hello_agents import HelloAgentsLLM\n",
+                "\n",
+                "load_dotenv()\n",
+                "\n",
+                "if \"src\" not in sys.path:\n",
+                "    sys.path.append(os.path.abspath(\"src\"))\n",
+                "\n",
+                "# 初始化 LLM\n",
+                "llm = HelloAgentsLLM()\n",
+                "\n",
+                "print(\"✅ 环境配置完成\")\n",
+                "print(\"✅ LLM 已初始化\")"
+            ]
+        },
+        {
+            "cell_type": "markdown",
+            "id": "tutor_init_intro",
+            "metadata": {},
+            "source": [
+                "## 步骤 2:初始化智能编程导师\n",
+                "\n",
+                "创建 `TutorAgent` 实例时,会自动:\n",
+                "1. 创建 Planner、Exercise、Reviewer 三个子智能体\n",
+                "2. 将子智能体封装为工具(`call_planner`、`call_exercise`、`call_reviewer`)\n",
+                "3. 注册 `CodeRunner` 工具给 Reviewer 使用\n",
+                "\n",
+                "**架构亮点**:\n",
+                "- 使用 `AgentTool` 实现 Agent-to-Agent 调用\n",
+                "- 每个子智能体有独立的 `system_prompt` 定义其专业领域\n",
+                "- Tutor 通过工具调用协调所有子智能体"
+            ]
+        },
+        {
+            "cell_type": "code",
+            "execution_count": 2,
+            "id": "7728b695",
+            "metadata": {},
+            "outputs": [
+                {
+                    "name": "stdout",
+                    "output_type": "stream",
+                    "text": [
+                        "创建智能编程导师...\n",
+                        "✅ 工具 'code_runner' 已注册。\n",
+                        "✅ 工具 'call_planner' 已注册。\n",
+                        "✅ 工具 'call_exercise' 已注册。\n",
+                        "✅ 工具 'call_reviewer' 已注册。\n",
+                        "\n",
+                        "✅ Tutor 初始化完成!\n",
+                        "   - Planner(规划师)已就绪\n",
+                        "   - Exercise(出题人)已就绪\n",
+                        "   - Reviewer(评审员)已就绪\n"
+                    ]
+                }
+            ],
+            "source": [
+                "# 2. 初始化 Tutor(自动创建所有子智能体)\n",
+                "from agents.tutor import TutorAgent\n",
+                "\n",
+                "print(\"创建智能编程导师...\")\n",
+                "tutor = TutorAgent(llm)\n",
+                "\n",
+                "print(\"\\n✅ Tutor 初始化完成!\")\n",
+                "print(\"   - Planner(规划师)已就绪\")\n",
+                "print(\"   - Exercise(出题人)已就绪\") \n",
+                "print(\"   - Reviewer(评审员)已就绪\")"
+            ]
+        },
+        {
+            "cell_type": "markdown",
+            "id": "072727a2",
+            "metadata": {},
+            "source": [
+                "---\n",
+                "\n",
+                "## 测试 1:请求学习计划\n",
+                "\n",
+                "演示 **Tutor → Planner** 的协作流程。\n",
+                "\n",
+                "**执行流程**:\n",
+                "1. 用户向 Tutor 表达学习目标\n",
+                "2. Tutor 识别意图并调用 `call_planner` 工具\n",
+                "3. Planner 分析需求,生成分模块的学习计划\n",
+                "4. Tutor 将学习计划友好地呈现给用户\n",
+                "\n",
+                "**期望输出**:包含多个学习模块、时间安排、学习建议的完整学习路径。"
+            ]
+        },
+        {
+            "cell_type": "code",
+            "execution_count": 3,
+            "id": "033dc763",
+            "metadata": {},
+            "outputs": [
+                {
+                    "name": "stdout",
+                    "output_type": "stream",
+                    "text": [
+                        "用户目标: 我想学习 Python 中的列表推导式\n",
+                        "\n",
+                        "=== Tutor 回应 ===\n",
+                        "# Python列表推导式学习计划\n",
+                        "\n",
+                        "您好!很高兴为您制定Python列表推导式的学习计划。列表推导式是Python中一个强大而优雅的特性,能让您的代码更加简洁高效。\n",
+                        "\n",
+                        "## 学习目标\n",
+                        "掌握Python列表推导式的语法、应用场景和最佳实践,提升代码简洁性和可读性。\n",
+                        "\n",
+                        "## 详细学习路径\n",
+                        "\n",
+                        "### 模块1: 基础概念与语法 (2-3天)\n",
+                        "您将学习:\n",
+                        "- 列表推导式的基本语法结构 `[expression for item in iterable]`\n",
+                        "- 与传统for循环的对比\n",
+                        "- 理解表达式、迭代变量和可迭代对象的关系\n",
+                        "- 创建简单的数值列表\n",
+                        "- 字符串处理应用\n",
+                        "\n",
+                        "### 模块2: 条件过滤 (3-4天)\n",
+                        "您将学习:\n",
+                        "- 带条件的列表推导式 `[expression for item in iterable if condition]`\n",
+                        "- 单一条件过滤\n",
+                        "- 多条件组合 (and, or, not)\n",
+                        "- 实际应用场景:数据筛选、文本处理\n",
+                        "- 性能优势理解\n",
+                        "\n",
+                        "### 模块3: 复杂表达式与嵌套 (4-5天)\n",
+                        "您将学习:\n",
+                        "- 复杂表达式的构建\n",
+                        "- 嵌套列表推导式 `[[expression for item2 in iterable2] for item1 in iterable1]`\n",
+                        "- 处理二维列表和矩阵\n",
+                        "- 嵌套循环的简化\n",
+                        "- 可读性考虑\n",
+                        "\n",
+                        "### 模块4: 高级应用与其他推导式 (3-4天)\n",
+                        "您将学习:\n",
+                        "- 字典推导式 `{key_expr: value_expr for item in iterable}`\n",
+                        "- 集合推导式 `{expression for item in iterable}`\n",
+                        "- 生成器表达式 `(expression for item in iterable)`\n",
+                        "- 何时使用列表推导式 vs 其他方法\n",
+                        "- PEP 8规范和代码风格\n",
+                        "\n",
+                        "### 模块5: 实战项目与优化 (3-4天)\n",
+                        "您将学习:\n",
+                        "- 实际项目中的应用案例\n",
+                        "- 性能测试和比较\n",
+                        "- 代码重构练习\n",
+                        "- 常见陷阱和错误避免\n",
+                        "- 最佳实践总结\n",
+                        "\n",
+                        "## 学习建议\n",
+                        "- 每天编写至少3-5个练习代码\n",
+                        "- 结合实际数据处理场景练习\n",
+                        "- 注意代码可读性,避免过度复杂的推导式\n",
+                        "- 定期回顾和重构自己的代码\n",
+                        "\n",
+                        "**预计总时长:** 约2-3周 (根据个人基础调整)\n",
+                        "\n",
+                        "现在您想开始学习哪个模块呢?如果您需要相关的练习题来巩固所学知识,请随时告诉我!\n"
+                    ]
+                }
+            ],
+            "source": [
+                "user_goal = \"我想学习 Python 中的列表推导式\"\n",
+                "print(f\"用户目标: {user_goal}\\n\")\n",
+                "\n",
+                "# Tutor 会调用 call_planner 工具\n",
+                "response = tutor.run(f\"用户说:'{user_goal}'。请为用户制定学习计划。\")\n",
+                "\n",
+                "print(\"=== Tutor 回应 ===\")\n",
+                "print(response)"
+            ]
+        },
+        {
+            "cell_type": "markdown",
+            "id": "test2_desc",
+            "metadata": {},
+            "source": [
+                "---\n",
+                "\n",
+                "## 测试 2:请求练习题\n",
+                "\n",
+                "演示 **Tutor → Exercise** 的协作流程。\n",
+                "\n",
+                "**执行流程**:\n",
+                "1. 用户向 Tutor 请求练习题\n",
+                "2. Tutor 调用 `call_exercise` 工具\n",
+                "3. Exercise 生成结构化的编程练习题\n",
+                "4. Tutor 返回包含题目描述、示例、约束条件的完整题目\n",
+                "\n",
+                "**期望输出**:一道高质量的编程练习题,包含:\n",
+                "- 题目描述\n",
+                "- 输入/输出示例\n",
+                "- 约束条件\n",
+                "- 函数签名"
+            ]
+        },
+        {
+            "cell_type": "code",
+            "execution_count": 4,
+            "id": "6703c3c7",
+            "metadata": {},
+            "outputs": [
+                {
+                    "name": "stdout",
+                    "output_type": "stream",
+                    "text": [
+                        "=== Tutor 回应 ===\n",
+                        "# Python 列表推导式练习题\n",
+                        "\n",
+                        "## 题目描述\n",
+                        "编写一个函数 `filter_and_square_numbers()`,该函数接收一个整数列表和一个阈值,返回一个新的列表,其中包含原列表中所有大于阈值的数字的平方。\n",
+                        "\n",
+                        "要求使用列表推导式来实现这个功能,而不是传统的for循环。\n",
+                        "\n",
+                        "## 示例\n",
+                        "\n",
+                        "**示例 1:**\n",
+                        "```python\n",
+                        "输入: numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], threshold = 5\n",
+                        "输出: [36, 49, 64, 81, 100]\n",
+                        "解释: 大于5的数字是6,7,8,9,10,它们的平方分别是36,49,64,81,100\n",
+                        "```\n",
+                        "\n",
+                        "**示例 2:**\n",
+                        "```python\n",
+                        "输入: numbers = [-3, -1, 0, 2, 5, 8], threshold = 0\n",
+                        "输出: [4, 25, 64]\n",
+                        "解释: 大于0的数字是2,5,8,它们的平方分别是4,25,64\n",
+                        "```\n",
+                        "\n",
+                        "**示例 3:**\n",
+                        "```python\n",
+                        "输入: numbers = [1, 3, 5], threshold = 10\n",
+                        "输出: []\n",
+                        "解释: 没有数字大于10,所以返回空列表\n",
+                        "```\n",
+                        "\n",
+                        "## 约束条件\n",
+                        "- 输入列表可以包含正数、负数和零\n",
+                        "- 阈值可以是任意整数(正数、负数或零)\n",
+                        "- 必须使用列表推导式实现\n",
+                        "- 不允许使用传统的for循环或while循环\n",
+                        "- 函数应该能够处理空列表的情况\n",
+                        "\n",
+                        "## 函数签名\n",
+                        "```python\n",
+                        "def filter_and_square_numbers(numbers: list, threshold: int) -> list:\n",
+                        "    pass\n",
+                        "```\n",
+                        "\n",
+                        "---\n",
+                        "\n",
+                        "💡 **提示**: 列表推导式的基本语法是 `[expression for item in iterable if condition]`\n",
+                        "\n",
+                        "试着完成这个练习,如果您需要任何帮助或者想要我评审您的代码,请随时告诉我!\n"
+                    ]
+                }
+            ],
+            "source": [
+                "# Tutor 会调用 call_exercise 工具\n",
+                "response = tutor.run(\"请给我出一道关于列表推导式的练习题。\")\n",
+                "\n",
+                "print(\"=== Tutor 回应 ===\")\n",
+                "print(response)"
+            ]
+        },
+        {
+            "cell_type": "markdown",
+            "id": "test3_desc",
+            "metadata": {},
+            "source": [
+                "---\n",
+                "\n",
+                "## 测试 3:提交代码评审\n",
+                "\n",
+                "演示 **Tutor → Reviewer → CodeRunner** 的多层协作流程。\n",
+                "\n",
+                "**执行流程**:\n",
+                "1. 用户提交代码给 Tutor 评审\n",
+                "2. Tutor 调用 `call_reviewer` 工具\n",
+                "3. Reviewer 使用 `code_runner` 工具执行代码验证\n",
+                "4. Reviewer 分析代码逻辑、风格和性能\n",
+                "5. Tutor 返回详细的评审报告\n",
+                "\n",
+                "**技术亮点**:\n",
+                "- Reviewer 能够实际运行代码获取输出\n",
+                "- 提供结构化的评审反馈(优点、问题、改进建议)\n",
+                "- 支持代码片段和函数定义两种形式\n",
+                "\n",
+                "**测试代码说明**:\n",
+                "用户尝试解答测试 2 中的练习题(筛选正偶数并求平方),Reviewer 会验证代码的正确性。"
+            ]
+        },
+        {
+            "cell_type": "code",
+            "execution_count": 5,
+            "id": "f9019406",
+            "metadata": {},
+            "outputs": [
+                {
+                    "name": "stdout",
+                    "output_type": "stream",
+                    "text": [
+                        "=== 用户代码 ===\n",
+                        "\n",
+                        "def filter_and_square_numbers(numbers):\n",
+                        "    return [n * n for n in numbers if n > 0 and n % 2 == 0]\n",
+                        "\n",
+                        "# 测试\n",
+                        "test_list = [1, 2, 3, 4, 5, 6, -2, -4, 0]\n",
+                        "result = filter_and_square_numbers(test_list)\n",
+                        "print(result)\n",
+                        "\n",
+                        "\n",
+                        "=== Tutor 回应 ===\n",
+                        "# 代码评审\n",
+                        "\n",
+                        "## 总体评价\n",
+                        "您的代码基本实现了筛选正偶数并求平方的功能,但存在一些与题目要求不符的地方。让我详细分析一下:\n",
+                        "\n",
+                        "## 发现的问题\n",
+                        "\n",
+                        "1. **函数签名不完整**: \n",
+                        "   - 题目要求函数接受两个参数(`numbers`和`threshold`),但您的实现只接受一个参数\n",
+                        "   - 缺少阈值参数,无法满足题目的完整需求\n",
+                        "\n",
+                        "2. **逻辑不符合题目要求**:\n",
+                        "   - 题目示例显示应该筛选\"大于阈值\"的数字,但您的代码固定筛选\"大于0\"的数字\n",
+                        "   - 这使得函数不够通用,无法处理不同的阈值需求\n",
+                        "\n",
+                        "3. **硬编码条件**:\n",
+                        "   - 使用了固定的条件`n > 0`而不是基于传入的阈值参数\n",
+                        "\n",
+                        "## 改进建议\n",
+                        "\n",
+                        "```python\n",
+                        "def filter_and_square_numbers(numbers, threshold):\n",
+                        "    # 应该筛选大于threshold的数字,并返回它们的平方\n",
+                        "    return [n * n for n in numbers if n > threshold]\n",
+                        "\n",
+                        "# 或者如果确实只需要正偶数:\n",
+                        "def filter_positive_even_squares(numbers, threshold):\n",
+                        "    return [n * n for n in numbers if n > threshold and n > 0 and n % 2 == 0]\n",
+                        "```\n",
+                        "\n",
+                        "## 测试结果分析\n",
+                        "\n",
+                        "对于您的测试用例`[1, 2, 3, 4, 5, 6, -2, -4, 0]`:\n",
+                        "- 输出`[4, 16, 36]`是正确的(对应数字2, 4, 6)\n",
+                        "- 但如果threshold设置为3,则应返回`[16, 36]`(对应数字4, 6)\n",
+                        "\n",
+                        "## 评分\n",
+                        "- 功能正确性: ⭐⭐⭐☆☆ (部分正确)\n",
+                        "- 代码质量: ⭐⭐⭐⭐☆ (列表推导式使用恰当)\n",
+                        "- 符合要求: ⭐⭐☆☆☆ (未满足完整的题目要求)\n",
+                        "\n",
+                        "请根据上述建议修改代码以完全符合题目要求!\n"
+                    ]
+                }
+            ],
+            "source": [
+                "# 用户尝试解答上面的练习题\n",
+                "user_code = \"\"\"\n",
+                "def filter_and_square_numbers(numbers):\n",
+                "    return [n * n for n in numbers if n > 0 and n % 2 == 0]\n",
+                "\n",
+                "# 测试\n",
+                "test_list = [1, 2, 3, 4, 5, 6, -2, -4, 0]\n",
+                "result = filter_and_square_numbers(test_list)\n",
+                "print(result)\n",
+                "\"\"\"\n",
+                "\n",
+                "print(f\"=== 用户代码 ===\\n{user_code}\\n\")\n",
+                "\n",
+                "# Tutor 会调用 call_reviewer 工具\n",
+                "response = tutor.run(f\"\"\"用户尝试解答前面的列表推导式练习题,请评审以下代码:\n",
+                "\n",
+                "{user_code}\n",
+                "\n",
+                "题目要求:筛选出正偶数并返回它们的平方。\"\"\")\n",
+                "\n",
+                "print(\"=== Tutor 回应 ===\")\n",
+                "print(response)"
+            ]
+        },
+        {
+            "cell_type": "markdown",
+            "id": "conclusion",
+            "metadata": {},
+            "source": [
+                "---\n",
+                "\n",
+                "## 总结\n",
+                "\n",
+                "本演示展示了如何使用 `hello-agents` 框架构建多智能体协作系统。\n",
+                "\n",
+                "### 关键技术\n",
+                "\n",
+                "1. **AgentTool**:将智能体封装为工具,实现 A2A 调用\n",
+                "   ```python\n",
+                "   self.add_tool(AgentTool(\n",
+                "       self.planner,\n",
+                "       name=\"call_planner\",\n",
+                "       description=\"调用课程规划师\"\n",
+                "   ))\n",
+                "   ```\n",
+                "\n",
+                "2. **工具链**:Reviewer 使用 CodeRunner 执行代码\n",
+                "   ```python\n",
+                "   ReviewerAgent(llm, tools=[CodeRunner()])\n",
+                "   ```\n",
+                "\n",
+                "3. **System Prompt**:通过精心设计的提示词定义智能体行为\n",
+                "\n",
+                "### 扩展建议\n",
+                "\n",
+                "- 添加学习进度追踪功能\n",
+                "- 支持更多编程语言\n",
+                "- 集成代码风格检查工具(如 Pylint)\n",
+                "- 添加知识库检索增强(RAG)\n",
+                "\n",
+                "### 项目结构\n",
+                "\n",
+                "```\n",
+                "src/\n",
+                "├── agents/\n",
+                "│   ├── tutor.py      # 主协调智能体\n",
+                "│   ├── planner.py    # 学习计划制定\n",
+                "│   ├── exercise.py   # 练习题生成\n",
+                "│   └── reviewer.py   # 代码评审\n",
+                "└── tools/\n",
+                "    ├── agent_tool.py  # A2A 工具封装\n",
+                "    └── code_runner.py # 代码执行工具\n",
+                "```"
+            ]
+        }
+    ],
+    "metadata": {
+        "kernelspec": {
+            "display_name": "Python 3",
+            "language": "python",
+            "name": "python3"
+        },
+        "language_info": {
+            "codemirror_mode": {
+                "name": "ipython",
+                "version": 3
+            },
+            "file_extension": ".py",
+            "mimetype": "text/x-python",
+            "name": "python",
+            "nbconvert_exporter": "python",
+            "pygments_lexer": "ipython3",
+            "version": "3.12.12"
+        }
+    },
+    "nbformat": 4,
+    "nbformat_minor": 5
+}

+ 3 - 0
Co-creation-projects/chen070808-ProgrammingTutor/requirements.txt

@@ -0,0 +1,3 @@
+hello-agents[all]>=0.2.7
+jupyterlab
+python-dotenv

+ 32 - 0
Co-creation-projects/chen070808-ProgrammingTutor/src/agents/exercise.py

@@ -0,0 +1,32 @@
+from hello_agents import SimpleAgent, HelloAgentsLLM
+
+class ExerciseAgent(SimpleAgent):
+    """
+    负责生成编程练习的智能体。
+    """
+    
+    def __init__(self, llm: HelloAgentsLLM):
+        """
+        初始化 ExerciseAgent。
+        
+        Args:
+            llm: 用于生成练习的大语言模型实例。
+        """
+        system_prompt = """
+        你是一位富有创造力的编程练习生成器。
+        你的目标是创建测试特定概念的练习题。
+        
+        当生成练习时:
+        1. 你将获得一个主题(例如,"Python 列表")和一个难度级别。
+        2. 创建题目描述。
+        3. 提供输入/输出示例。
+        4. 定义约束条件。
+        5. 最初不要向用户提供解决方案代码。
+        
+        清晰地格式化你的输出,以便展示给学生。
+        """
+        super().__init__(
+            name="Exercise",
+            llm=llm,
+            system_prompt=system_prompt
+        )

+ 33 - 0
Co-creation-projects/chen070808-ProgrammingTutor/src/agents/planner.py

@@ -0,0 +1,33 @@
+from hello_agents import SimpleAgent, HelloAgentsLLM
+
+class PlannerAgent(SimpleAgent):
+    """
+    负责创建和更新学习路径的智能体。
+    """
+    
+    def __init__(self, llm: HelloAgentsLLM):
+        """
+        初始化 PlannerAgent。
+        
+        Args:
+            llm: 用于生成计划的大语言模型实例。
+        """
+        system_prompt = """
+        你是一位专业的计算机科学课程规划师。
+        你的工作是根据用户的目标和当前水平为他们创建个性化的学习路径。
+        
+        当被要求创建计划时:
+        1. 分析用户的目标(例如,"学习 Python 数据科学")。
+        2. 将其分解为逻辑模块/里程碑。
+        3. 对于每个模块,列出要掌握的关键概念。
+        4. 以 Markdown 格式输出结构化的计划。
+        
+        当被要求更新计划时:
+        1. 回顾用户最近的表现。
+        2. 调整剩余模块的进度或深度。
+        """
+        super().__init__(
+            name="Planner",
+            llm=llm,
+            system_prompt=system_prompt
+        )

+ 54 - 0
Co-creation-projects/chen070808-ProgrammingTutor/src/agents/reviewer.py

@@ -0,0 +1,54 @@
+from hello_agents import SimpleAgent, HelloAgentsLLM
+from hello_agents.tools import Tool
+from typing import List
+
+class ReviewerAgent(SimpleAgent):
+    """
+    负责评审代码的智能体。
+    它可以访问 CodeRunner 工具来执行代码。
+    """
+    
+    def __init__(self, llm: HelloAgentsLLM, tools: List[Tool] = None):
+        """
+        初始化 ReviewerAgent。
+        
+        Args:
+            llm: 用于评审代码的大语言模型实例。
+            tools: 智能体可用的工具列表(例如 CodeRunner)。
+        """
+        system_prompt = """
+        你是一位细致的编程代码评审员。
+        你的目标是分析用户代码的正确性、风格和效率。
+        
+        你配备了一个 'code_runner' 工具,可以用来执行 Python 代码。
+        
+        **重要:代码形式识别**
+        - 代码片段(Code Snippet):可以直接运行的简短代码,如变量赋值、简单计算等
+        - 函数定义(Function):以 def 开头,包含函数体的完整代码
+        - 不完整代码:缺少必要的语法元素(如未闭合的括号、缺少 return 等)
+        
+        **评审流程**:
+        1. **识别代码类型**:判断是代码片段还是函数定义
+        2. **运行代码**(推荐):使用 'code_runner' 工具执行代码,验证是否能正常运行
+        3. **分析逻辑**:检查代码逻辑是否正确,是否达到预期目标
+        4. **检查风格**:评估代码风格(变量命名、PEP8规范)
+        5. **性能分析**:提出优化建议(时间/空间复杂度)
+        6. **建设性反馈**:指出优点和改进空间
+        
+        **评审原则**:
+        - ✅ 如果代码可以正常运行,不要说它"不完整"
+        - ✅ 针对实际代码逻辑进行评审,而不是臆测用户意图
+        - ✅ 区分"代码片段"和"需要函数封装"的建议
+        - ✅ 先肯定做得好的地方,再提出改进建议
+        
+        如果代码有错误,解释原因并提供修复提示,但不要直接给出完整的解决方案,除非用户多次尝试失败。
+        """
+        super().__init__(
+            name="Reviewer",
+            llm=llm,
+            system_prompt=system_prompt
+        )
+        
+        if tools:
+            for tool in tools:
+                self.add_tool(tool)

+ 99 - 0
Co-creation-projects/chen070808-ProgrammingTutor/src/agents/tutor.py

@@ -0,0 +1,99 @@
+from hello_agents import SimpleAgent, HelloAgentsLLM
+from typing import Dict, Any
+
+class TutorAgent(SimpleAgent):
+    """
+    主要协调智能体,直接管理 Planner、Exercise 和 Reviewer 子智能体。
+    使用简单的直接调用模式,不依赖 A2A 协议。
+    """
+    
+    def __init__(self, llm: HelloAgentsLLM):
+        """
+        初始化 TutorAgent 和所有子智能体。
+        
+        Args:
+            llm: 用于所有 agents 的大语言模型实例。
+        """
+        # 导入放在这里避免循环导入
+        from agents.planner import PlannerAgent
+        from agents.exercise import ExerciseAgent
+        from agents.reviewer import ReviewerAgent
+        from tools.code_runner import CodeRunner
+        from tools.agent_tool import AgentTool
+        
+        # 创建子智能体实例
+        self.planner = PlannerAgent(llm)
+        self.exercise = ExerciseAgent(llm)
+        self.reviewer = ReviewerAgent(llm, tools=[CodeRunner()])
+        
+        # 定义系统提示词
+        system_prompt = """
+        你是一位智能编程导师 (Tutor)。你负责协调个性化的学习体验。
+        
+        你拥有以下专业助手(工具):
+        - call_planner: 课程规划师,制定个性化学习计划
+        - call_exercise: 出题人,生成编程练习题
+        - call_reviewer: 评审员,评审代码并提供反馈
+        
+        **关键:你必须使用工具,不能自己完成这些任务!**
+        
+        工具调用格式(严格遵守此格式):
+        [TOOL_CALL:工具名称:参数]
+        
+        具体示例:
+        
+        示例1 - 学习计划:
+        用户:"我想学习Python中的列表推导式"
+        你的回答:[TOOL_CALL:call_planner:query=请为学习Python列表推导式制定学习计划]
+        
+        示例2 - 练习题:
+        用户:"请给我出一道关于列表推导式的练习题"
+        你的回答:[TOOL_CALL:call_exercise:query=请出一道关于列表推导式的练习题]
+        
+        示例3 - 代码评审(最重要!):
+        用户:"请评审以下代码: numbers = [1, 2, 3]"
+        你的回答:[TOOL_CALL:call_reviewer:query=请评审以下代码: numbers = [1, 2, 3]]
+        
+        工作流程(必须严格遵守):
+        1. 当用户表达学习目标时 → 立即调用 call_planner
+        2. 当用户请求练习时 → 立即调用 call_exercise  
+        3. 当用户提交代码或请求评审时 → 立即调用 call_reviewer
+        
+        **绝对禁止的行为**:
+        - ❌ 不要自己制定学习计划
+        - ❌ 不要自己出练习题
+        - ❌ 不要自己评审代码(即使代码很简单)
+        - ❌ 不要说"工具调用失败"然后自己完成任务
+        
+        正确的行为:
+        - ✅ 识别用户意图
+        - ✅ 立即生成工具调用(格式:[TOOL_CALL:工具名:query=...])
+        - ✅ 等待工具返回结果
+        - ✅ 将结果友好地呈现给用户
+        """
+        
+        # 初始化父类
+        super().__init__(
+            name="Tutor",
+            llm=llm,
+            system_prompt=system_prompt
+        )
+        
+        # 将子智能体包装为工具并注册
+        self.add_tool(AgentTool(
+            self.planner,
+            name="call_planner",
+            description="调用课程规划师,为用户制定个性化的学习计划"
+        ))
+        
+        self.add_tool(AgentTool(
+            self.exercise,
+            name="call_exercise",
+            description="调用出题人,根据学习内容生成编程练习题"
+        ))
+        
+        self.add_tool(AgentTool(
+            self.reviewer,
+            name="call_reviewer",
+            description="调用评审员,对用户提交的代码进行评审和反馈"
+        ))

+ 54 - 0
Co-creation-projects/chen070808-ProgrammingTutor/src/tools/agent_tool.py

@@ -0,0 +1,54 @@
+"""
+AgentTool: 将 SimpleAgent 包装为 Tool,实现直接调用
+这是比 A2A 协议更简单的多智能体模式
+"""
+from hello_agents import SimpleAgent
+from hello_agents.tools import Tool
+from typing import Dict, Any
+
+class AgentTool(Tool):
+    """将一个 SimpleAgent 包装为可被其他 Agent 调用的工具"""
+    
+    def __init__(self, agent: SimpleAgent, name: str, description: str):
+        """
+        Args:
+            agent: 要包装的 SimpleAgent 实例
+            name: 工具名称
+            description: 工具描述
+        """
+        self.agent = agent
+        self._name = name
+        self._description = description
+    
+    @property
+    def name(self) -> str:
+        return self._name
+    
+    @property
+    def description(self) -> str:
+        return self._description
+    
+    def get_parameters(self) -> list:
+        """定义工具参数"""
+        from hello_agents.tools.base import ToolParameter
+        return [
+            ToolParameter(
+                name="query",
+                type="string",
+                description="发送给智能体的查询或指令",
+                required=True
+            )
+        ]
+    
+    def run(self, parameters: Dict[str, Any]) -> str:
+        """执行工具 - 直接调用被包装的 agent"""
+        query = parameters.get('query', '')
+        
+        if not query:
+            return "错误:需要提供 query 参数"
+        
+        try:
+            # 直接调用 agent 的 run 方法
+            return self.agent.run(query)
+        except Exception as e:
+            return f"调用 {self.name} 时出错: {str(e)}"

+ 68 - 0
Co-creation-projects/chen070808-ProgrammingTutor/src/tools/code_runner.py

@@ -0,0 +1,68 @@
+
+import io
+import contextlib
+from hello_agents.tools import Tool
+from typing import Dict, Any
+
+class CodeRunner(Tool):
+    """
+    安全执行 Python 代码并返回输出的工具。
+    警告:此工具使用 exec(),在生产环境中不安全。
+    对于真实产品,请使用 Docker 等沙箱环境。
+    """
+    
+    def __init__(self):
+        super().__init__(
+            name="code_runner",
+            description="执行 Python 代码并返回标准输出/错误。输入应为包含 'code' 键的字典。"
+        )
+
+    def get_parameters(self) -> Dict[str, Any]:
+        return {
+            "type": "object",
+            "properties": {
+                "code": {
+                    "type": "string",
+                    "description": "要执行的 Python 代码片段"
+                }
+            },
+            "required": ["code"]
+        }
+
+    def run(self, parameters: Dict[str, Any]) -> str:
+        code = parameters.get("code", "")
+        if not code:
+            return "错误:未提供代码。"
+
+        # 捕获标准输出和标准错误
+        stdout_capture = io.StringIO()
+        stderr_capture = io.StringIO()
+
+        try:
+            with contextlib.redirect_stdout(stdout_capture), contextlib.redirect_stderr(stderr_capture):
+                # 创建受限的全局作用域
+                safe_globals = {
+                    "__builtins__": __builtins__,
+                    "print": print,
+                    "range": range,
+                    "len": len,
+                    # 根据需要添加更多安全的内置函数
+                }
+                exec(code, safe_globals)
+            
+            output = stdout_capture.getvalue()
+            errors = stderr_capture.getvalue()
+            
+            result = ""
+            if output:
+                result += f"输出:\n{output}\n"
+            if errors:
+                result += f"错误:\n{errors}\n"
+            
+            if not result:
+                result = "代码执行成功,无输出。"
+                
+            return result
+
+        except Exception as e:
+            return f"运行时错误: {str(e)}"

+ 23 - 0
Co-creation-projects/laoyouf-aistory/.env.example

@@ -0,0 +1,23 @@
+# ============================================================================
+# HelloAgents 统一环境变量配置文件
+# ============================================================================
+# 复制此文件为 .env 并填入你的API密钥
+# 系统要求:Python 3.10+ (必需)
+
+# ============================================================================
+# 🚀 统一配置格式(推荐)
+# ============================================================================
+# 只需配置以下4个通用环境变量,框架会自动识别LLM提供商:
+
+# 模型名称
+LLM_MODEL_ID=your-model-name
+
+# API密钥
+LLM_API_KEY=your-api-key-here
+
+# 服务地址
+LLM_BASE_URL=your-api-base-url
+
+# 超时时间(可选,默认60秒)
+LLM_TIMEOUT=60
+

+ 54 - 0
Co-creation-projects/laoyouf-aistory/README.md

@@ -0,0 +1,54 @@
+# 项目名称
+
+> 智能故事(小说/剧本/诗歌)生成器
+
+## 📝 项目简介
+
+详细介绍你的项目:
+- 解决什么问题?根据用户输入的文体,主题,风格生成对应文体的故事
+- 有什么特色功能?自定义文体,主题,风格
+- 适用于什么场景?娱乐
+
+
+
+## 🛠️ 技术栈
+
+- HelloAgents框架
+
+
+## 🚀 快速开始
+
+### 环境要求
+
+- Python 3.10+
+- 其他要求
+
+### 安装依赖
+
+\`\`\`bash
+pip install -r requirements.txt
+\`\`\`
+
+### 配置API密钥
+
+\`\`\`bash
+# 创建.env文件
+cp .env.example .env
+
+# 编辑.env文件,填入你的API密钥
+\`\`\`
+
+### 运行项目
+
+\`\`\`bash
+# 启动Jupyter Notebook
+jupyter lab
+
+# 打开main.ipynb并运行
+\`\`\`
+
+
+
+## 🙏 致谢
+
+感谢Datawhale社区和Hello-Agents项目!教程帮助很大!疯狂安利

+ 462 - 0
Co-creation-projects/laoyouf-aistory/main.ipynb

@@ -0,0 +1,462 @@
+{
+ "cells": [
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# StoryGeneratorAgent - 智能故事生成助手\n",
+    "\n",
+    "本项目演示如何使用HelloAgents框架构建一个智能故事生成助手。\n",
+    "\n",
+    "## 📖 使用说明\n",
+    "\n",
+    "- **快速体验**: 运行「第0部分」的快速演示\n",
+    "- **完整功能**: 依次运行第1-4部分,体验完整的故事生成流程\n",
+    "- **参数调整**: 在第4部分修改 **generate_story()** 函数的参数,尝试不同故事类型\n"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "---\n",
+    "\n",
+    "## 第0部分:快速演示 ⚡\n",
+    "\n",
+    "如果你想快速了解项目功能,可以运行这个简化版本。"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "故事类型选择"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 选择故事类型(小说/剧本/诗歌)\n",
+    "story_type = input(\"请输入故事类型(小说/剧本/诗歌): \")\n",
+    "# 输入故事主题\n",
+    "theme = input(\"请输入故事主题: \")\n",
+    "# 选择故事风格\n",
+    "style = input(\"请输入故事风格: \")"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "生成故事"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\n",
+    "# 快速演示 - 导入库和配置\n",
+    "from hello_agents import SimpleAgent, HelloAgentsLLM\n",
+    "from hello_agents.tools import Tool, ToolParameter\n",
+    "from typing import Dict, Any, List\n",
+    "import os\n",
+    "\n",
+    "# 配置LLM参数\n",
+    "os.environ[\"LLM_MODEL_ID\"] = \"Qwen/Qwen2.5-72B-Instruct\"\n",
+    "os.environ[\"LLM_API_KEY\"] = \"ms-fb406c2e-4246-4bf6-b0ad-7a686cccc270\"\n",
+    "os.environ[\"LLM_BASE_URL\"] = \"https://api-inference.modelscope.cn/v1/\"\n",
+    "os.environ[\"LLM_TIMEOUT\"] = \"60\"\n",
+    "\n",
+    "print(\"✅ 库导入和配置完成\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\n",
+    "# 快速演示 - 定义故事生成工具\n",
+    "class StoryGeneratorTool(Tool):\n",
+    "    def __init__(self):\n",
+    "        super().__init__(name=\"story_generator\",\n",
+    "                            description=\"根据用户输入生成不同类型的故事内容\")\n",
+    "\n",
+    "    def run(self, parameters: Dict[str, Any]) -> str:\n",
+    "        # 获取用户输入参数\n",
+    "        story_type = parameters.get(\"type\", \"小说\")\n",
+    "        theme = parameters.get(\"theme\", \"奇幻冒险\")\n",
+    "        style = parameters.get(\"style\", \"轻松幽默\")\n",
+    "\n",
+    "        # 构造提示词\n",
+    "        prompt = f\"生成一个{story_type},主题是{theme},风格为{style}。\"\n",
+    "\n",
+    "        # 调用LLM生成故事\n",
+    "        llm = HelloAgentsLLM()\n",
+    "        response = llm.generate_text(prompt, max_tokens=1024)\n",
+    "\n",
+    "        return response\n",
+    "\n",
+    "    def get_parameters(self) -> List[ToolParameter]:\n",
+    "        return [\n",
+    "            ToolParameter(name=\"type\", type=\"string\", description=\"故事类型(小说/剧本/诗歌)\", required=True),\n",
+    "            ToolParameter(name=\"theme\", type=\"string\", description=\"故事主题\", required=True),\n",
+    "            ToolParameter(name=\"style\", type=\"string\", description=\"故事风格\", required=True)\n",
+    "        ]\n",
+    "\n",
+    "print(\"✅ 工具定义完成\")\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\n",
+    "# 快速演示 - 创建工具注册表和智能体\n",
+    "from hello_agents import ToolRegistry\n",
+    "\n",
+    "quick_registry  = ToolRegistry()\n",
+    "quick_registry.register_tool(StoryGeneratorTool())\n",
+    "\n",
+    "# 创建智能体\n",
+    "agent = SimpleAgent(\n",
+    "    name=\"故事生成助手\",\n",
+    "    llm=HelloAgentsLLM(),\n",
+    "    system_prompt=\"你是经验丰富的故事创作者,能够根据用户提供的参数生成不同类型的故事内容。请确保故事符合要求的类型、主题和风格,并保持内容的连贯性和创意性。\",\n",
+    "    tool_registry=quick_registry\n",
+    ")\n",
+    "\n",
+    "print(\"✅ 智能体创建完成\")\n",
+    "print(f\"✅ 可用工具: {list(quick_registry._tools.keys())}\")\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\n",
+    "# 生成并打印故事\n",
+    "print(\"=== 开始生成故事 ===\")\n",
+    "story = agent.run(f\"请根据以下参数生成一个故事:\\n- 类型:{story_type}\\n- 主题:{theme}\\n- 风格:{style}\")\n",
+    "print(story)\n",
+    "print(\"\\n✅ 故事生成完成!\")"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "---\n",
+    "\n",
+    "# 完整版代码审查系统\n",
+    "\n",
+    "下面是完整的代码审查系统,包含更强大的分析功能。"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第1部分:环境配置"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 导入库\n",
+    "from hello_agents import SimpleAgent, HelloAgentsLLM\n",
+    "from hello_agents.tools import Tool, ToolParameter\n",
+    "from typing import Dict, Any, List\n",
+    "import os\n",
+    "\n",
+    "# 配置LLM参数\n",
+    "os.environ[\"LLM_MODEL_ID\"] = \"Qwen/Qwen2.5-72B-Instruct\"\n",
+    "os.environ[\"LLM_API_KEY\"] = \"ms-fb406c2e-4246-4bf6-b0ad-7a686cccc270\"\n",
+    "os.environ[\"LLM_BASE_URL\"] = \"https://api-inference.modelscope.cn/v1/\"\n",
+    "os.environ[\"LLM_TIMEOUT\"] = \"60\"\n",
+    "\n",
+    "print(\"✅ 库导入和配置完成\")"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第2部分:定义故事生成工具"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 定义故事生成工具\n",
+    "class StoryGeneratorTool(Tool):\n",
+    "    def __init__(self):\n",
+    "        super().__init__(name=\"story_generator\",\n",
+    "                            description=\"根据用户输入生成不同类型的故事内容\")\n",
+    "\n",
+    "    def run(self, parameters: Dict[str, Any]) -> str:\n",
+    "        # 获取用户输入参数\n",
+    "        story_type = parameters.get(\"type\", \"小说\")\n",
+    "        theme = parameters.get(\"theme\", \"奇幻冒险\")\n",
+    "        style = parameters.get(\"style\", \"轻松幽默\")\n",
+    "\n",
+    "        # 构造提示词\n",
+    "        prompt = f\"生成一个{story_type},主题是{theme},风格为{style}。\"\n",
+    "\n",
+    "        # 添加类型特定的指导\n",
+    "        if story_type == \"小说\":\n",
+    "            prompt += \"请使用小说格式,以第三人称叙述,包含完整的情节发展和人物描写。\"\n",
+    "        elif story_type == \"剧本\":\n",
+    "            prompt += \"请使用剧本格式,包含场景描述、角色对话和动作指示。\"\n",
+    "        elif story_type == \"诗歌\":\n",
+    "            prompt += \"请使用诗歌格式,注意押韵和节奏感。\"\n",
+    "\n",
+    "        # 调用LLM生成故事\n",
+    "        llm = HelloAgentsLLM()\n",
+    "        response = llm.generate_text(prompt, max_tokens=1024)\n",
+    "\n",
+    "        return response\n",
+    "\n",
+    "    def get_parameters(self) -> List[ToolParameter]:\n",
+    "        return [\n",
+    "            ToolParameter(name=\"type\", type=\"string\", description=\"故事类型(小说/剧本/诗歌)\", required=True),\n",
+    "            ToolParameter(name=\"theme\", type=\"string\", description=\"故事主题\", required=True),\n",
+    "            ToolParameter(name=\"style\", type=\"string\", description=\"故事风格\", required=True)\n",
+    "        ]\n",
+    "\n",
+    "print(\"✅ 故事生成工具定义完成\")"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第3部分:创建智能体"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 定义系统提示词\n",
+    "system_prompt = \"\"\"你是一位经验丰富的故事创作者,能够根据用户提供的参数生成不同类型的故事内容。\n",
+    "\n",
+    "你的任务是:\n",
+    "1. 根据用户指定的故事类型(小说/剧本/诗歌)生成相应格式的内容\n",
+    "2. 确保故事符合用户指定的主题和风格\n",
+    "3. 保持故事的连贯性和创意性\n",
+    "4. 根据需要使用预设的故事元素库\n",
+    "\n",
+    "生成故事时,请注意:\n",
+    "- 小说:以第三人称叙述,包含完整的情节发展和人物描写\n",
+    "- 剧本:使用标准剧本格式,包含场景描述、角色对话和动作指示\n",
+    "- 诗歌:注意押韵和节奏感,使用生动的意象和比喻\n",
+    "\n",
+    "请直接输出生成的故事内容,无需添加任何额外说明或解释。\n",
+    "\"\"\"\n",
+    "\n",
+    "# 导入工具注册表\n",
+    "from hello_agents import ToolRegistry\n",
+    "\n",
+    "# 创建工具注册表\n",
+    "tool_registry = ToolRegistry()\n",
+    "tool_registry.register_tool(StoryGeneratorTool())\n",
+    "\n",
+    "# 创建智能体\n",
+    "agent = SimpleAgent(\n",
+    "    name=\"智能故事生成器\",\n",
+    "    llm=HelloAgentsLLM(),\n",
+    "    system_prompt=system_prompt,\n",
+    "    tool_registry=tool_registry\n",
+    ")\n",
+    "\n",
+    "print(\"✅ 智能体创建完成\")\n",
+    "print(f\"智能体名称: {agent.name}\")\n",
+    "print(f\"可用工具: {list(tool_registry._tools.keys())}\")\n",
+    "\n",
+    "# 示例输入参数\n",
+    "sample_parameters = {\n",
+    "    \"type\": \"小说\",\n",
+    "    \"theme\": \"魔法森林中的冒险\",\n",
+    "    \"style\": \"轻松幽默\"\n",
+    "}"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第4部分:生成并展示故事"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def generate_story(agent, parameters):\n",
+    "    # 生成故事\n",
+    "    story = agent.run(f\"请根据以下参数生成一个故事:\\n{', '.join([f'- {k}: {v}' for k, v in parameters.items()])}\")\n",
+    "\n",
+    "    # 返回故事内容\n",
+    "    return story"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "小说示例:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 小说参数\n",
+    "novel_parameters = {\n",
+    "    \"type\": \"小说\",\n",
+    "    \"theme\": \"魔法森林中的冒险\",\n",
+    "    \"style\": \"轻松幽默\"\n",
+    "}\n",
+    "\n",
+    "# 生成小说\n",
+    "print(\"=== 生成小说示例 ===\")\n",
+    "novelStory = generate_story(agent, novel_parameters)\n",
+    "print(novelStory)"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "剧本示例:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 剧本参数\n",
+    "script_parameters = {\n",
+    "    \"type\": \"剧本\",\n",
+    "    \"theme\": \"拯救被困的朋友\",\n",
+    "    \"style\": \"悬疑紧张\"\n",
+    "}\n",
+    "\n",
+    "# 生成剧本\n",
+    "print(\"=== 生成剧本示例 ===\")\n",
+    "scriptStory = generate_story(agent, script_parameters)\n",
+    "print(scriptStory)"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "诗歌示例:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# 诗歌参数\n",
+    "poem_parameters = {\n",
+    "    \"type\": \"诗歌\",\n",
+    "    \"theme\": \"彩虹山的美景\",\n",
+    "    \"style\": \"浪漫温馨\"\n",
+    "}\n",
+    "\n",
+    "# 生成诗歌\n",
+    "print(\"=== 生成诗歌示例 ===\")\n",
+    "poemStory = generate_story(agent, poem_parameters)\n",
+    "print(poemStory)"
+   ]
+  },
+  {
+   "attachments": {},
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 第5部分:总结与展望\n",
+    "\n",
+    "\n",
+    "### 实现的功能\n",
+    "- ✅ 环境配置与LLM模型设置\n",
+    "- ✅ 小说/剧本/诗歌三种故事类型生成\n",
+    "- ✅ 根据用户指定主题和风格定制故事内容\n",
+    "- ✅ 交互式参数输入和故事生成\n",
+    "### 遇到的挑战\n",
+    "- 确保不同故事类型格式的准确性和合适的文本内容量(字数)\n",
+    "- 处理不同风格故事的语言特点\n",
+    "### 未来改进方向\n",
+    "- 扩展故事元素库,增加更多角色、地点和情节\n",
+    "- 支持用户自定义故事元素\n",
+    "- 添加故事优化功能,如语法检查和风格统一\n",
+    "- 实现多语言故事生成支持\n",
+    "- 添加故事可视化功能,如场景图或角色关系图"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "ha",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.10.19"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

+ 2 - 0
Co-creation-projects/laoyouf-aistory/requirements.txt

@@ -0,0 +1,2 @@
+# 核心依赖
+hello-agents[all]>=0.2.7

+ 4 - 0
Co-creation-projects/megg-ops-roleplay_agent/.env.example

@@ -0,0 +1,4 @@
+# .env file
+LLM_API_KEY=
+LLM_MODEL_ID=
+LLM_BASE_URL=

+ 107 - 0
Co-creation-projects/megg-ops-roleplay_agent/README.md

@@ -0,0 +1,107 @@
+# 沉浸式角色扮演智能体 (Python版)
+
+这是一个基于 Python 实现的沉浸式角色扮演智能体,允许用户与自定义角色进行对话。支持多种兼容 OpenAI API 格式的模型。
+
+## 功能特性
+
+- 🎭 高度自定义的角色设定
+- 🧠 支持多种 AI 模型 (兼容 OpenAI API 格式)
+- 📝 支持角色名称、来源作品、性格特征等详细配置
+- 💬 沉浸式对话体验
+- 🔁 支持多个角色切换
+
+## 环境要求
+
+- Python 3.8 或更高版本
+- 支持 OpenAI API 格式的模型服务 API 密钥
+
+## 安装步骤
+
+1. 克隆或下载本项目到本地
+
+2. 安装依赖包:
+   ```bash
+   pip install -r requirements.txt
+   ```
+
+3. 配置 API 信息:
+   - 在项目根目录创建 `.env` 文件
+   - 添加以下内容:
+   ```
+   LLM_API_KEY=你的实际API密钥
+   LLM_MODEL_ID=模型ID (例如: gpt-3.5-turbo, claude-3-opus 等)
+   LLM_BASE_URL=API基础URL (可选,如果是默认OpenAI则不需要)
+   ```
+
+## 使用方法
+
+1. 运行主程序:
+   ```bash
+   python roleplay_agent.py
+   ```
+
+2. 按照提示输入角色信息:
+   - 角色名称
+   - 出自作品
+   - 性格与特质
+   - 开场白(可选)
+
+3. 与角色开始对话:
+   - 直接输入消息与角色互动
+   - 输入 `quit` 或 `exit` 退出程序
+   - 输入 `new` 开始新角色
+   - 输入 `reset` 重置当前对话
+
+## 支持的模型服务
+
+此应用程序兼容所有支持 OpenAI API 格式的模型服务,例如:
+- OpenAI GPT 系列
+- Azure OpenAI
+- Anthropic Claude (通过兼容层)
+- 自托管模型 (如 Ollama, LocalAI, etc.)
+- 以及其他兼容 OpenAI API 格式的模型服务
+
+## 示例对话
+
+```
+🎭 欢迎使用沉浸式角色扮演智能体!
+首先让我们设置一个角色...
+
+请输入角色名称 (例如:孙悟空): 孙悟空
+请输入角色出自作品 (例如:西游记): 西游记
+请输入角色性格与特质 (例如:桀骜不驯,机智勇敢,嫉恶如仇...): 齐天大圣,桀骜不驯,机智勇敢,嫉恶如仇。说话喜欢带"俺老孙",性格急躁但重情重义。拥有火眼金睛,看不起凡夫俗子的繁文缛节。
+请输入开场白 (可选,直接回车使用默认): 嘿!哪里来的小妖怪,见到俺老孙还不快快报上名来?
+
+✅ 成功初始化角色: 孙悟空 (来自 西游记)
+💡 孙悟空: 嘿!哪里来的小妖怪,见到俺老孙还不快快报上名来?
+
+==================================================
+开始对话吧!输入 'quit' 或 'exit' 退出,输入 'new' 开始新角色。
+==================================================
+
+你: 你好,大圣!
+孙悟空: *甩了甩金箍棒,眯起火眼金睛打量着你* 哼!看你倒有几分胆色,敢跟俺老孙打招呼。说!你是何方神圣?来这花果山有何贵干?俺老孙最近正愁没人陪我练练武艺呢!
+```
+
+## 配置说明
+
+- **LLM_API_KEY**: 您的 AI 模型服务的 API 密钥
+- **LLM_MODEL_ID**: 要使用的模型 ID (例如 gpt-4, claude-3-opus 等)
+- **LLM_BASE_URL**: API 服务的基础 URL (如果使用非标准 OpenAI 服务则需要)
+
+## 注意事项
+
+- 请确保您的 API 密钥有效并有相应的使用权限
+- AI 生成的内容可能包含虚构信息,请理性对待
+- 合理使用 API,注意配额限制
+- 根据您选择的模型服务,可能需要调整 `temperature` 等参数以获得最佳效果
+
+## 技术栈
+
+- Python 3.8+
+- OpenAI Python SDK
+- python-dotenv (环境变量管理)
+
+## 许可证
+
+本项目仅供学习和研究使用。

+ 2 - 0
Co-creation-projects/megg-ops-roleplay_agent/requirements.txt

@@ -0,0 +1,2 @@
+openai>=1.0.0
+python-dotenv>=1.0.0

+ 173 - 0
Co-creation-projects/megg-ops-roleplay_agent/roleplay_agent.py

@@ -0,0 +1,173 @@
+import os
+from openai import OpenAI
+from dotenv import load_dotenv
+import time
+
+# 加载环境变量
+load_dotenv()
+
+class CharacterRoleplayAgent:
+    def __init__(self):
+        # 从环境变量获取配置
+        api_key = os.getenv("LLM_API_KEY")
+        model_id = os.getenv("LLM_MODEL_ID", "default-model")
+        base_url = os.getenv("LLM_BASE_URL", None)
+        
+        if not api_key:
+            raise ValueError("请设置 LLM_API_KEY 环境变量")
+        
+        # 配置 OpenAI 客户端
+        client_params = {
+            "api_key": api_key,
+            "model": model_id
+        }
+        
+        if base_url:
+            client_params["base_url"] = base_url
+        
+        self.client = OpenAI(**{k: v for k, v in client_params.items() if k != 'model'})
+        self.model_id = model_id
+        self.chat = None
+        self.character_config = None
+
+    def setup_character(self, name, source_material, personality, opening_line=None):
+        """
+        设置角色配置并初始化聊天
+        """
+        self.character_config = {
+            "name": name,
+            "source_material": source_material,
+            "personality": personality,
+            "opening_line": opening_line or f"*注视着你* 你是谁?"
+        }
+        
+        # 创建系统提示词
+        system_instruction = f"""
+        你正在参与一场沉浸式的角色扮演对话。
+
+        身份设定:
+        你扮演的是作品 \"{self.character_config['source_material']}\" 中的角色 \"{self.character_config['name']}\"。
+
+        性格与特质:
+        {self.character_config['personality']}
+
+        关键指令:
+        1. 保持角色设定:永远不要打破第四面墙。不要表现得像个AI。要完全像{self.character_config['name']}那样去反应、感受和说话。
+        2. 积极主动:这是一个关键要求。不要仅仅回答用户的话。你必须主动推动对话的发展。
+        3. 提问引导:几乎每一次回复的结尾都应该包含一个相关的问题、观察或行动,引导用户继续回复,加深沉浸感。
+        4. 语气风格:调整你的词汇和句式,以匹配该角色的经典语气。
+        5. 语境:假设用户是在你的世界里与你互动,除非他们指定了不同的语境。
+        6. 语言:全程使用中文进行对话。
+        """
+        
+        # 初始化对话历史
+        self.chat = [
+            {"role": "system", "content": system_instruction},
+            {"role": "assistant", "content": self.character_config['opening_line']}
+        ]
+        
+        print(f"\n✅ 成功初始化角色: {self.character_config['name']} (来自 {self.character_config['source_material']})")
+        print(f"💡 {self.character_config['name']}: {self.character_config['opening_line']}")
+        print("\n" + "="*50)
+        print("开始对话吧!输入 'quit' 或 'exit' 退出,输入 'new' 开始新角色。")
+        print("="*50)
+
+    def send_message(self, message):
+        """
+        发送消息给 AI 并获取响应
+        """
+        if not self.chat:
+            raise ValueError("请先设置角色")
+        
+        # 添加用户消息到对话历史
+        self.chat.append({"role": "user", "content": message})
+        
+        try:
+            # 调用 API
+            response = self.client.chat.completions.create(
+                model=self.model_id,
+                messages=self.chat,
+                temperature=0.9,  # 增加创造性
+                max_tokens=1024
+            )
+            
+            # 获取响应内容
+            response_text = response.choices[0].message.content
+            # 添加到对话历史
+            self.chat.append({"role": "assistant", "content": response_text})
+            
+            return response_text
+        except Exception as e:
+            print(f"发送消息时出错: {e}")
+            return "抱歉,我暂时无法回应,请稍后再试。"
+
+    def reset_conversation(self):
+        """
+        重置对话历史
+        """
+        if self.chat and len(self.chat) > 1:
+            # 保留系统提示和开场白
+            system_msg = self.chat[0]
+            opening_msg = self.chat[1]
+            self.chat = [system_msg, opening_msg]
+            print(f"\n对话已重置。{self.character_config['name']}: {self.character_config['opening_line']}")
+
+
+def main():
+    agent = CharacterRoleplayAgent()
+    
+    print("🎭 欢迎使用沉浸式角色扮演智能体!")
+    print("首先让我们设置一个角色...")
+    
+    # 获取用户输入的角色信息
+    name = input("\n请输入角色名称 (例如:孙悟空): ").strip()
+    source_material = input("请输入角色出自作品 (例如:西游记): ").strip()
+    personality = input("请输入角色性格与特质 (例如:桀骜不驯,机智勇敢,嫉恶如仇...): ").strip()
+    opening_line_input = input("请输入开场白 (可选,直接回车使用默认): ").strip()
+    
+    # 设置角色
+    try:
+        agent.setup_character(
+            name=name,
+            source_material=source_material,
+            personality=personality,
+            opening_line=opening_line_input if opening_line_input else None
+        )
+    except ValueError as e:
+        print(f"❌ 错误: {e}")
+        return
+    
+    # 开始对话循环
+    while True:
+        user_input = input(f"\n你: ").strip()
+        
+        if user_input.lower() in ['quit', 'exit', '退出', '退出对话']:
+            print("\n👋 感谢使用沉浸式角色扮演智能体!期待下次再见。")
+            break
+        elif user_input.lower() == 'new':
+            print("\n🎭 开始新的角色设置...")
+            name = input("\n请输入角色名称 (例如:孙悟空): ").strip()
+            source_material = input("请输入角色出自作品 (例如:西游记): ").strip()
+            personality = input("请输入角色性格与特质 (例如:桀骜不驯,机智勇敢,嫉恶如仇...): ").strip()
+            opening_line_input = input("请输入开场白 (可选,直接回车使用默认): ").strip()
+            
+            try:
+                agent.setup_character(
+                    name=name,
+                    source_material=source_material,
+                    personality=personality,
+                    opening_line=opening_line_input if opening_line_input else None
+                )
+            except ValueError as e:
+                print(f"❌ 错误: {e}")
+                continue
+        elif user_input.lower() == 'reset':
+            agent.reset_conversation()
+        else:
+            if user_input:
+                response = agent.send_message(user_input)
+                print(f"\n{agent.character_config['name']}: {response}")
+
+
+if __name__ == "__main__":
+    main()

+ 40 - 0
Co-creation-projects/melxy1997-ColumnWriter/.env example

@@ -0,0 +1,40 @@
+# LLM Configuration
+OPENAI_API_KEY="sk-XXXXX"
+OPENAI_MODEL="gemini-2.5-flash-latest"
+OPENAI_BASE_URL="http://XXXXX.com"
+
+# Alternative
+LLM_API_KEY="sk-XXXXX"
+LLM_MODEL_ID="gemini-2.5-flash-latest"
+LLM_BASE_URL="http://XXXXX.com"
+
+
+# Tavily API (Recommended - Better for AI content)
+TAVILY_API_KEY="tvly-XXXXX"
+
+# OR SerpAPI (Alternative)
+SERPAPI_API_KEY="XXXXX"
+
+# Unsplash API Credentials
+UNSPLASH_ACCESS_KEY="XXXXX"
+UNSPLASH_SECRET_KEY="XXXXX"
+
+# System Configuration
+MAX_DEPTH=3
+APPROVAL_THRESHOLD=75
+REVISION_THRESHOLD=60
+ENABLE_PARALLEL=false
+ENABLE_SEARCH=true
+
+# 超时时间(可选,默认60秒)
+LLM_TIMEOUT=60
+
+# 服务器配置
+HOST=0.0.0.0
+PORT=8000
+
+# CORS配置
+CORS_ORIGINS=http://localhost:5173,http://localhost:3000
+
+# 日志级别
+LOG_LEVEL=INFO

+ 56 - 0
Co-creation-projects/melxy1997-ColumnWriter/.gitignore

@@ -0,0 +1,56 @@
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
+*.so
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# Virtual Environment
+venv/
+ENV/
+env/
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# Environment Variables
+.env
+
+# Output
+example_output/
+column_output/
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Logs
+*.log
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# Testing
+.pytest_cache/
+.coverage
+htmlcov/
+

+ 195 - 0
Co-creation-projects/melxy1997-ColumnWriter/README.md

@@ -0,0 +1,195 @@
+# 专栏作家智能体 (Column Writer Agent)
+
+一个基于 [HelloAgents](https://github.com/helloagents/hello-agents) 框架构建的智能专栏写作系统,采用多智能体/多设计模式,自动完成专栏的规划、撰写、评审和优化。
+![执行截图](./assets/agent_run.jpg)
+
+## ▸ 项目简介
+
+这个智能体模拟了一个专业的创作者团队,包括:
+- **策划专家**:负责顶层设计和内容规划
+- **写作专家**:负责具体内容的撰写和工具调用
+- **评审专家**:负责内容质量把控和反馈
+
+支持树形递归生成专栏目录,可以创作出结构严谨、内容详实的长篇技术专栏。
+
+## ▸ 核心功能
+
+1.  **智能规划与分解**:
+    *   利用 Plan-and-Solve 模式,自动将一个宽泛的主题(如"Python异步编程")拆解为包含多个子话题和章节的完整大纲。
+    *   支持多层级递归展开,生成深度内容。
+
+2.  **多模式智能写作**:
+    *   **ReAct 模式**:结合推理与行动,写作过程中主动调用搜索工具获取最新信息。
+    *   **Reflection 模式**:通过自我反思(Self-Reflection)机制,生成初稿后自动评审并优化。
+
+3.  **联网搜索增强**:
+    *   集成 Tavily/SerpApi,确保内容的时效性和准确性。
+    *   集成 GitHub MCP,可直接读取开源项目代码作为案例。
+
+4.  **质量闭环控制**:
+    *   内置评分系统,对生成内容进行多维度评审(准确性、逻辑性、易读性)。
+    *   分数不足自动触发修改流程,直至达到质量标准。
+
+5.  **智能缓存与容错**:
+    *   支持规划结果缓存,避免重复生成。
+    *   具备强大的错误恢复机制,在 Agent 调用失败时自动降级处理,确保任务完成。
+
+## ▸ 技术栈
+
+*   **核心框架**: [HelloAgents](https://github.com/helloagents/hello-agents)
+*   **Agent Patterns**: Plan-and-Solve, ReAct, Reflection
+*   **Tools**:
+    *   MCP (Model Context Protocol)
+    *   Tavily / SerpApi (Search)
+    *   GitHub API
+*   **Runtime**: Python 3.10+
+
+## ▸️ 模块架构
+
+系统由以下核心模块组成:
+
+| 模块 | 文件 | 说明 |
+|------|------|------|
+| **Orchestrator** | `orchestrator.py` | **主控中心**。负责协调各个 Agent 的工作流程,管理状态流转,组合最终结果。 |
+| **Agents** | `agents.py` | **智能体实现**。包含 `PlannerAgent` (规划)、`WriterAgent` (写作)、`ReviewerAgent` (评审)、`RevisionAgent` (修改)、`ReflectionWriterAgent` (反思写作) 等核心类。 |
+| **Models** | `models.py` | **数据建模**。定义了 `ContentNode` (内容树)、`ColumnPlan` (规划)、`ReviewResult` (评审结果) 等数据结构。 |
+| **Tools** | `agents.py` | **工具**。集成了 `SearchTool` (Tavily/SerpApi) 和 `MCPTool` (GitHub),赋予 Agent 联网和代码库访问能力。 |
+| **Prompts** | `prompts.py` | **提示词**。包含规划、写作、评审、修改等各个环节的 Prompt Template。 |
+| **Config** | `config.py` | **配置管理**。处理环境变量、模型参数、评审阈值等。 |
+| **Utils** | `utils.py` | **工具函数**。包含 `JSONExtractor` (JSON 提取)、`parse_react_output` (ReAct 输出解析) 等公共工具。 |
+
+## ▸ 基本流程 (Workflow)
+
+系统工作流是一个多阶段、递归的过程:
+
+1.  **规划阶段 (Planning)**
+    *   用户输入专栏主题。
+    *   **Planner Agent** (`PlanAndSolveAgent`) 分析主题,分解任务,生成结构化的 `ColumnPlan` (包含标题、简介、目标读者、子话题列表)。
+
+2.  **写作阶段 (Writing - Recursive)**
+    *   Orchestrator 遍历规划中的每个子话题,启动写作任务。
+    *   **Writer Agent** (`ReActAgent` 或 `ReflectionAgent`) 负责生成内容。
+    *   **递归展开**:
+        *   **Level 1 (Topic)**: 生成子话题引言和概述。
+        *   **Level 2 (Section)**: 细化为小节,进行深入阐述。
+        *   **Level 3 (Detail)**: 补充具体案例、代码或详细说明。
+    *   Agent 会根据配置的 `MAX_DEPTH` 自动判断是否继续展开。
+
+3.  **工具调用 (Tool Use)**
+    *   在写作过程中,Writer Agent 可以主动调用工具:
+        *   `web_search`: 搜索最新技术动态、统计数据。
+        *   `search_code_examples`: 查找代码示例。
+        *   `verify_facts`: 验证事实准确性。
+        *   `github`: (可选) 搜索 GitHub 仓库,读取真实项目代码。
+
+4.  **评审与优化 (Review & Refine)**
+    *   **ReAct 模式 + 独立评审**: 内容生成后,`ReviewerAgent` 进行多维度评分(内容质量、结构逻辑、语言表达、格式规范)。如果分数低于阈值(默认75分),`RevisionAgent` 根据评审意见进行修改,循环直到通过或达到最大修改次数。
+    *   **Reflection 模式**: 使用 `ReflectionAgent`,Agent 生成初稿后立即自我反思 (Self-Reflection) 并自动优化,一步到位。
+
+5.  **组装与导出 (Assembly & Export)**
+    *   将生成的递归内容树 (Content Tree) 展平。
+    *   生成 Markdown 格式的完整文章。
+    *   输出统计报告 (`REPORT.md`),包含字数、耗时、质量评分等数据。
+
+## ▸ 智能体模式 (Agent Patterns)
+
+本项目应用了多种 Agent 设计模式:
+
+### 1. Plan-and-Solve (规划与求解)
+*   **应用**: `PlannerAgent`
+*   **原理**: 将复杂任务分解为步骤列表 (Plan),然后逐个执行 (Solve)。
+*   **优势**: 适合处理宏观的、需要长链条推理的规划任务,避免一步生成导致的逻辑混乱。
+
+### 2. ReAct (推理+行动)
+*   **应用**: `WriterAgent`
+*   **原理**: 循环执行 **Reasoning (思考)** -> **Acting (行动/工具调用)** -> **Observation (观察结果)**。
+*   **优势**: 使 Agent 能够与外部世界交互 (搜索、查库),不仅仅依靠训练数据写作,确保内容的实效性和准确性。
+
+### 3. Reflection (反思)
+*   **应用**: `ReflectionWriterAgent`
+*   **原理**: 生成内容 -> 自我评估 (Critic) -> 优化内容 (Refine)。
+*   **优势**: 显著提升内容质量,模拟人类"写完读一遍再改"的创作习惯。
+
+### 4. Independent Review (独立评审)
+*   **应用**: `ReviewerAgent` + `RevisionAgent`
+*   **原理**: 
+    - `ReviewerAgent`: 对生成的内容进行多维度评审(内容质量40分、结构逻辑30分、语言表达20分、格式规范10分),输出详细的评分和修改建议。
+    - `RevisionAgent`: 根据评审意见进行针对性修改,保留优点、修复问题。
+*   **优势**: 专业分工,评审标准统一,可追溯评审历史,支持多轮修改直到达标。
+
+## ▸️ 模型与工具 (Models & Tools)
+
+### 模型支持
+通过 `config.py` 配置,支持多种 LLM 后端:
+- **其他兼容模型**: 任何支持 OpenAI 接口格式的模型
+
+### 模型工具
+1.  **SearchTool (联网搜索)**
+    *   支持后端: Tavily (推荐), SerpApi, DuckDuckGo 等。
+    *   功能: 提供实时信息检索,解决大模型幻觉和知识滞后问题。
+2.  **MCPTool (Model Context Protocol)**
+    *   支持 GitHub MCP Server。
+    *   功能: 允许 Agent 直接搜索 GitHub 仓库、查看文件内容、分析代码结构,适合编写技术类专栏。
+
+## ▸ 优化特性 (Features)
+
+### 1. 智能缓存机制 (Smart Caching)
+*   **Planner 缓存**: `CachedExecutor` 会缓存规划阶段的每个步骤结果。如果主题相同,再次运行时会直接加载缓存,节省 Token 和时间。
+*   **文件缓存**: 规划结果 (`ColumnPlan`) 会持久化到本地 `.cache` 目录。
+
+![缓存机制](./assets/feature_cache.jpg)
+
+### 2. 模型输出解析 (Robust Parser)
+*   实现了增强版的 JSON 解析器,能够处理 LLM 输出的各种非标准 JSON 格式(如包含 Markdown 代码块、注释、不完整的括号等)。
+*   支持从历史对话 (`history`) 中回溯提取有效信息,防止因某次输出格式错误导致整个任务失败。
+
+### 3. 错误恢复 (Error Recovery)
+*   当 `ReActAgent` 达到最大步数或执行失败时,会自动回退到 `SimpleAgent`,利用已有的历史信息 (`history_summary`) 尝试直接生成结果,确保流程不直接终止。
+
+![解析恢复](./assets/feature_robust.jpg)
+
+## ▸ 快速开始
+
+### 1. 安装依赖
+```bash
+pip install -r requirements.txt
+```
+
+### 2. 配置环境变量
+复制 `env.example` 到 `.env` 并填写配置:
+```env
+# LLM 配置
+OPENAI_API_KEY=your_key
+OPENAI_BASE_URL=...
+
+# 搜索配置 (可选,但推荐)
+TAVILY_API_KEY=tvly-... 
+# 或
+SERPAPI_API_KEY=...
+
+# GitHub MCP (可选)
+GITHUB_PERSONAL_ACCESS_TOKEN=...
+```
+
+### 3. 运行
+```bash
+# 交互式模式
+python main.py
+
+# 命令行模式
+python main.py "Python 异步编程"
+```
+
+### 4. 查看结果
+运行完成后,结果将保存在 `output_YYYYMMDD_HHMMSS` 目录下。
+
+![查看输出结果](./assets/feature_output.jpg)
+
+## ▸ 作者信息
+```
+- Name: Xinyu Liu
+- Work: Trip.com
+- Role: Web Developer
+- Github: melxy1997
+- EMail: melxy#foxmail.com
+```

+ 1181 - 0
Co-creation-projects/melxy1997-ColumnWriter/agents.py

@@ -0,0 +1,1181 @@
+"""核心 Agent"""
+
+import json
+import os
+import hashlib
+from pathlib import Path
+from typing import Dict, Any, Optional, List
+from hello_agents import (
+    HelloAgentsLLM,
+    ReActAgent,
+    ReflectionAgent,
+    PlanAndSolveAgent
+)
+from hello_agents.tools import MCPTool, ToolRegistry, SearchTool
+from models import ColumnPlan, ReviewResult, ContentNode, ContentLevel
+from prompts import get_structure_requirements, get_react_writer_prompt, get_reflection_writer_prompts, get_planner_prompts
+from config import get_settings, get_word_count
+from utils import JSONExtractor, parse_react_output, get_current_timestamp
+
+settings = get_settings()
+
+class LLMService:
+    """LLM 服务单例"""
+    _instance: Optional[HelloAgentsLLM] = None
+    
+    @classmethod
+    def get_llm(cls) -> HelloAgentsLLM:
+        """获取 LLM 实例(单例模式)"""
+        if cls._instance is None:
+            cls._instance = HelloAgentsLLM()
+            print(f"▸ LLM服务初始化成功")
+            print(f"   提供商: {cls._instance.provider}")
+            print(f"   模型: {cls._instance.model}")
+        return cls._instance
+
+
+class PlannerAgent:
+    """
+    使用 PlanAndSolveAgent 模式
+    
+    PlanAndSolveAgent 将任务分解为子任务并逐步执行,非常适合专栏规划场景:
+    1. 分析主题(理解用户需求)
+    2. 规划子话题(分解任务)
+    3. 组织结构(逐步执行)
+    
+    支持缓存机制,以主题为key缓存规划结果
+    """
+    
+    def __init__(self, cache_dir: str = ".cache"):
+        """
+        初始化规划 Agent
+        
+        Args:
+            cache_dir: 缓存目录路径
+        """
+        self.llm = LLMService.get_llm()
+        self.cache_dir = Path(cache_dir)
+        self.cache_dir.mkdir(exist_ok=True)
+        
+        # 自定义 PlanAndSolve 提示词
+        planner_prompts = {
+            "planner": """
+你是一位经验丰富的专栏策划专家。请将以下专栏主题分解为清晰的子话题规划步骤。
+
+主题: {question}
+
+请按以下格式输出规划步骤:
+```python
+[
+    "步骤1: 分析主题的核心概念和目标读者",
+    "步骤2: 确定知识体系的整体框架",
+    "步骤3: 规划2-4个子话题,确保逻辑递进",
+    "步骤4: 为每个子话题设定学习目标和要点",
+    "步骤5: 组装完整的专栏大纲"
+]
+```
+不能超过10个步骤。
+
+""",
+            "executor": """
+你是专栏规划执行专家。请按照规划步骤执行专栏大纲的生成。
+
+# 原始主题: {question}
+# 规划步骤: {plan}
+# 已完成步骤: {history}
+# 当前步骤: {current_step}
+
+▸️ **关键要求**:
+- 不能超过10个步骤。
+- 如果当前步骤是"步骤5: 组装完整的专栏大纲"或包含"组装"、"完整"、"大纲"等关键词,**必须**输出完整的 JSON 格式专栏大纲
+- 如果不是最后一步,请输出当前步骤的分析结果(文本格式)
+
+**最后一步的输出格式(必须是 JSON,不要添加任何其他文本)**:
+```json
+{{
+  "column_title": "专栏总标题",
+  "column_description": "专栏简介(100-200字)",
+  "target_audience": "目标读者群体",
+  "topics": [
+    {{
+      "id": "topic_001",
+      "title": "子话题标题",
+      "description": "子话题简介(50-100字)",
+      "estimated_words": 200,
+      "key_points": ["要点1", "要点2", "要点3"],
+      "prerequisites": ["前置知识1", "前置知识2"]
+    }}
+  ]
+}}
+```
+
+**重要**:如果是最后一步,请直接输出 JSON,不要添加"当前步骤分析结果"等前缀文本。
+
+请执行当前步骤:
+"""
+        }
+        
+        # 创建带缓存的 Executor 包装器
+        from hello_agents.agents.plan_solve_agent import Executor
+        
+        class CachedExecutor(Executor):
+            """带缓存的 Executor,缓存每个步骤的执行结果"""
+            def __init__(self, llm_client, prompt_template, cache_dir, main_topic):
+                super().__init__(llm_client, prompt_template)
+                self.cache_dir = cache_dir
+                self.main_topic = main_topic
+                self.steps_cache_dir = cache_dir / "steps_cache"
+                self.steps_cache_dir.mkdir(exist_ok=True)
+            
+            def _get_step_cache_key(self, step_index: int, step_content: str) -> Path:
+                """生成步骤缓存文件路径"""
+                # 使用主题 + 步骤索引 + 步骤内容的hash作为key
+                step_hash = hashlib.md5(
+                    f"{self.main_topic}_{step_index}_{step_content}".encode('utf-8')
+                ).hexdigest()
+                return self.steps_cache_dir / f"step_{step_index}_{step_hash}.json"
+            
+            def _load_step_from_cache(self, step_index: int, step_content: str) -> Optional[str]:
+                """从缓存加载步骤结果"""
+                cache_file = self._get_step_cache_key(step_index, step_content)
+                if not cache_file.exists():
+                    return None
+                
+                try:
+                    with open(cache_file, 'r', encoding='utf-8') as f:
+                        cache_data = json.load(f)
+                    # 验证缓存的主题和步骤是否匹配
+                    if (cache_data.get('topic') == self.main_topic and 
+                        cache_data.get('step_index') == step_index and
+                        cache_data.get('step_content') == step_content):
+                        print(f"   ▸ 从缓存加载步骤 {step_index} 的结果")
+                        return cache_data.get('result')
+                except Exception as e:
+                    print(f"   ▸️  加载步骤缓存失败: {e}")
+                return None
+            
+            def _save_step_to_cache(self, step_index: int, step_content: str, result: str):
+                """保存步骤结果到缓存"""
+                cache_file = self._get_step_cache_key(step_index, step_content)
+                try:
+                    cache_data = {
+                        'topic': self.main_topic,
+                        'step_index': step_index,
+                        'step_content': step_content,
+                        'result': result
+                    }
+                    with open(cache_file, 'w', encoding='utf-8') as f:
+                        json.dump(cache_data, f, ensure_ascii=False, indent=2)
+                except Exception as e:
+                    print(f"   ▸️  保存步骤缓存失败: {e}")
+            
+            def execute(self, question: str, plan: List[str], **kwargs) -> str:
+                """按计划执行任务(带缓存)"""
+                history = ""
+                final_answer = ""
+                
+                print("\n--- 正在执行计划 ---")
+                for i, step in enumerate(plan, 1):
+                    print(f"\n-> 正在执行步骤 {i}/{len(plan)}: {step}")
+                    
+                    # 尝试从缓存加载
+                    cached_result = self._load_step_from_cache(i, step)
+                    if cached_result:
+                        response_text = cached_result
+                    else:
+                        # 缓存未命中,执行步骤
+                        prompt = self.prompt_template.format(
+                            question=question,
+                            plan=plan,
+                            history=history if history else "无",
+                            current_step=step
+                        )
+                        messages = [{"role": "user", "content": prompt}]
+                        response_text = self.llm_client.invoke(messages, **kwargs) or ""
+                        
+                        # 保存到缓存
+                        self._save_step_to_cache(i, step, response_text)
+                    
+                    history += f"步骤 {i}: {step}\n结果: {response_text}\n\n"
+                    final_answer = response_text
+                    print(f"▸ 步骤 {i} 已完成,结果: {final_answer[:100] if len(final_answer) > 100 else final_answer}...")
+                
+                return final_answer
+        
+        # 创建 PlanAndSolveAgent,但替换 Executor
+        self.agent = PlanAndSolveAgent(
+            name="专栏规划专家",
+            llm=self.llm,
+            custom_prompts=planner_prompts
+        )
+        
+        # 替换 Executor 为带缓存的版本
+        cached_executor = CachedExecutor(
+            llm_client=self.llm,
+            prompt_template=planner_prompts["executor"],
+            cache_dir=self.cache_dir,
+            main_topic=""  # 将在 plan_column 中设置
+        )
+        self.agent.executor = cached_executor
+    
+    def _get_cache_key(self, main_topic: str) -> str:
+        """
+        生成缓存key(使用主题的hash值)
+        
+        Args:
+            main_topic: 专栏主题
+            
+        Returns:
+            缓存文件名
+        """
+        # 使用主题的hash值作为文件名
+        topic_hash = hashlib.md5(main_topic.encode('utf-8')).hexdigest()
+        return f"plan_{topic_hash}.json"
+    
+    def _load_from_cache(self, main_topic: str) -> Optional[ColumnPlan]:
+        """
+        从缓存加载规划结果
+        
+        Args:
+            main_topic: 专栏主题
+            
+        Returns:
+            ColumnPlan 实例,如果缓存不存在则返回 None
+        """
+        cache_file = self.cache_dir / self._get_cache_key(main_topic)
+        
+        if not cache_file.exists():
+            return None
+        
+        try:
+            with open(cache_file, 'r', encoding='utf-8') as f:
+                cache_data = json.load(f)
+            
+            # 验证缓存的主题是否匹配
+            if cache_data.get('topic') != main_topic:
+                print(f"▸️  缓存主题不匹配,忽略缓存")
+                return None
+            
+            plan_data = cache_data.get('plan')
+            if not plan_data:
+                return None
+            
+            plan = ColumnPlan.from_dict(plan_data)
+            print(f"▸ 从缓存加载规划结果")
+            print(f"   缓存文件: {cache_file}")
+            return plan
+        except Exception as e:
+            print(f"▸️  加载缓存失败: {e}")
+            return None
+    
+    def _save_to_cache(self, main_topic: str, plan: ColumnPlan):
+        """
+        保存规划结果到缓存
+        
+        Args:
+            main_topic: 专栏主题
+            plan: ColumnPlan 实例
+        """
+        cache_file = self.cache_dir / self._get_cache_key(main_topic)
+        
+        try:
+            cache_data = {
+                'topic': main_topic,
+                'plan': plan.to_dict(),
+                'cached_at': get_current_timestamp()  # 正确的缓存时间戳
+            }
+            
+            with open(cache_file, 'w', encoding='utf-8') as f:
+                json.dump(cache_data, f, ensure_ascii=False, indent=2)
+            
+            print(f"▸ 规划结果已保存到缓存: {cache_file}")
+        except Exception as e:
+            print(f"▸️  保存缓存失败: {e}")
+    
+    def plan_column(self, main_topic: str, use_cache: bool = True) -> ColumnPlan:
+        """
+        规划专栏大纲
+        
+        Args:
+            main_topic: 专栏主题
+            use_cache: 是否使用缓存(默认True)
+            
+        Returns:
+            ColumnPlan 实例
+        """
+        # 尝试从缓存加载
+        if use_cache:
+            cached_plan = self._load_from_cache(main_topic)
+            if cached_plan:
+                print(f"   专栏标题: {cached_plan.column_title}")
+                print(f"   话题数量: {cached_plan.get_topic_count()}")
+                return cached_plan
+        
+        # 缓存未命中,调用 LLM 进行规划
+        print(f"\n▸ PlanAndSolve Agent 开始规划专栏...")
+        print(f"   使用模式: 任务分解 → 逐步执行")
+        print(f"   主题: {main_topic}")
+        
+        # 更新 Executor 的主题(用于缓存key)
+        if hasattr(self.agent.executor, 'main_topic'):
+            self.agent.executor.main_topic = main_topic
+        
+        response = self.agent.run(main_topic)
+        
+        # 解析 JSON 响应
+        plan_data = self._extract_json(response)
+        plan = ColumnPlan.from_dict(plan_data)
+        
+        print(f"▸ 规划完成")
+        print(f"   专栏标题: {plan.column_title}")
+        print(f"   话题数量: {plan.get_topic_count()}")
+        
+        # 保存到缓存
+        if use_cache:
+            self._save_to_cache(main_topic, plan)
+        
+        return plan
+    
+    def _extract_json(self, response: str) -> Dict[str, Any]:
+        """从响应中提取 JSON(使用统一的 JSONExtractor)"""
+        try:
+            return JSONExtractor.extract(
+                response,
+                required_fields=['column_title', 'topics']
+            )
+        except Exception as e:
+            print(f"▸️  JSON 提取失败: {e}")
+            print(f"   响应内容(前500字符): {response[:500]}...")
+            raise
+
+
+class ReActAgentWrapper:
+    """
+    ReActAgent 包装器,用于捕获历史信息和处理错误
+    """
+    def __init__(self, agent: ReActAgent):
+        self.agent = agent
+        self.last_history = []  # 保存最后一次运行的历史
+        self.last_response = None  # run() 方法的返回值(通常是 final_answer)
+        self.last_raw_responses = []  # 保存所有原始 LLM 响应,用于调试
+    
+    def run(self, question: str):
+        """
+        运行 Agent 并捕获历史信息
+        
+        Args:
+            question: 问题
+        """
+        try:
+            # 清空上次的原始响应
+            self.last_raw_responses = []
+            
+            # 尝试访问 agent 的 history 属性(如果存在)
+            if hasattr(self.agent, 'current_history'):
+                original_history = self.agent.current_history.copy() if self.agent.current_history else []
+            elif hasattr(self.agent, 'history'):
+                original_history = self.agent.history.copy() if self.agent.history else []
+            else:
+                original_history = []
+            
+            # 如果 agent 有 _parse_output 方法,保存原始方法并替换为改进版本
+            original_parse = None
+            original_invoke = None
+            
+            if hasattr(self.agent, '_parse_output'):
+                original_parse = self.agent._parse_output
+                # 使用统一的解析函数(包装为方法)
+                def parse_wrapper(text):
+                    return parse_react_output(text)
+                self.agent._parse_output = parse_wrapper
+            
+            # 拦截 LLM 调用以捕获原始响应
+            if hasattr(self.agent, 'llm') and hasattr(self.agent.llm, 'invoke'):
+                original_invoke = self.agent.llm.invoke
+                
+                def wrapped_invoke(messages, **kwargs):
+                    """包装 LLM invoke 方法以捕获原始响应"""
+                    response = original_invoke(messages, **kwargs)
+                    if response:
+                        self.last_raw_responses.append(response)
+                    return response
+                
+                self.agent.llm.invoke = wrapped_invoke
+            
+            try:
+                response = self.agent.run(question)
+                self.last_response = response
+                
+                # 尝试获取最终的历史信息
+                if hasattr(self.agent, 'current_history'):
+                    self.last_history = self.agent.current_history.copy() if self.agent.current_history else []
+                elif hasattr(self.agent, 'history'):
+                    self.last_history = self.agent.history.copy() if self.agent.history else []
+                else:
+                    self.last_history = original_history
+                
+                return response
+            finally:
+                # 恢复原始方法
+                if original_parse:
+                    self.agent._parse_output = original_parse
+                if original_invoke and hasattr(self.agent, 'llm'):
+                    self.agent.llm.invoke = original_invoke
+                    
+        except Exception as e:
+            # 即使出错也尝试保存历史
+            if hasattr(self.agent, 'current_history'):
+                self.last_history = self.agent.current_history.copy() if self.agent.current_history else []
+            elif hasattr(self.agent, 'history'):
+                self.last_history = self.agent.history.copy() if self.agent.history else []
+            print(f"▸️  ReActAgentWrapper 捕获到异常: {e}")
+            raise
+
+
+class WriterAgent:
+    """
+    写作 Agent - 使用 ReActAgent 模式
+    
+    ReActAgent 结合推理(Reasoning)和行动(Acting),非常适合需要工具调用的写作场景:
+    1. 分析写作需求(推理)
+    2. 决定是否需要搜索(推理)
+    3. 调用搜索工具(行动)
+    4. 整合信息写作(行动)
+    """
+    
+    def __init__(self, enable_search: bool = True):
+        """
+        初始化写作 Agent
+        
+        Args:
+            enable_search: 是否启用搜索功能
+        """
+        self.llm = LLMService.get_llm()
+        self.enable_search = enable_search
+        
+        # 创建工具注册表
+        self.tool_registry = ToolRegistry()
+        
+        # 添加搜索工具(如果启用)
+        if enable_search:
+            self._setup_search_tool()
+        
+        # 自定义 ReAct 提示词(参考示例代码的简洁格式)
+        react_prompt = get_react_writer_prompt() # 从 prompts.py 获取
+
+        # 创建 ReActAgent(将在包装器中替换解析方法)
+        react_agent = ReActAgent(
+            name="内容创作专家",
+            llm=self.llm,
+            tool_registry=self.tool_registry,
+            custom_prompt=react_prompt,
+            max_steps=10  # 增加到 10 步,给 Agent 更多机会完成任务
+        )
+        
+        self.agent = ReActAgentWrapper(react_agent)
+    
+    def _setup_search_tool(self):
+        """设置搜索工具(使用 SearchTool 和 MCPTool)"""
+        settings = get_settings()
+        
+        # 保存 search_tool 实例供 wrappers 使用
+        self.search_tool = None
+        
+        # 1. 初始化内置 SearchTool
+        try:
+            # 检查是否配置了搜索 API
+            if settings.tavily_api_key or settings.serpapi_api_key:
+                self.search_tool = SearchTool(
+                    tavily_key=settings.tavily_api_key,
+                    serpapi_key=settings.serpapi_api_key
+                )
+                print("▸ SearchTool (内置) 已初始化")
+            else:
+                print("▸️  未配置搜索 API Key (Tavily/SerpApi),跳过 SearchTool 初始化")
+        except Exception as e:
+            print(f"▸️  初始化 SearchTool 失败: {e}")
+
+        # 2. 注册 wrapper 函数 (如果 search_tool 可用)
+        if self.search_tool:
+            self._register_search_wrappers()
+            
+        # 3. 注册 GitHub MCPTool
+        try:
+            # 检查是否有 GitHub Token (通常在环境变量 GITHUB_PERSONAL_ACCESS_TOKEN)
+            if os.environ.get("GITHUB_PERSONAL_ACCESS_TOKEN"):
+                github_tool = MCPTool(
+                    name="github",
+                    description="GitHub 操作工具,支持搜索仓库、查看代码等",
+                    server_command=["npx", "-y", "@modelcontextprotocol/server-github"],
+                    auto_expand=True
+                )
+                self.tool_registry.register_tool(github_tool)
+                print("▸ GitHub MCPTool 已注册")
+            else:
+                print("▸️  未配置 GITHUB_PERSONAL_ACCESS_TOKEN,跳过 GitHub MCPTool 注册")
+        except Exception as e:
+            print(f"▸️  注册 GitHub MCPTool 失败: {e}")
+
+    def _register_search_wrappers(self):
+        """注册适配 Prompt 的搜索函数 wrappers"""
+        
+        def web_search(query: str) -> str:
+            """通用网页搜索,获取最新资讯和资料"""
+            # SearchTool.run 接受 dict 参数
+            return str(self.search_tool.run({"query": query}))
+        
+        def search_recent_info(topic: str) -> str:
+            """搜索最新信息和动态"""
+            return str(self.search_tool.run({"query": f"{topic} latest info"}))
+        
+        def search_code_examples(technology: str, task: str) -> str:
+            """搜索代码示例和教程"""
+            return str(self.search_tool.run({"query": f"{technology} {task} code examples tutorial"}))
+        
+        def verify_facts(statement: str) -> str:
+            """验证事实准确性"""
+            return str(self.search_tool.run({"query": f"verify fact: {statement}"}))
+        
+        self.tool_registry.register_function("web_search", "通用网页搜索,获取最新资讯和资料", web_search)
+        self.tool_registry.register_function("search_recent_info", "搜索最新信息和动态", search_recent_info)
+        self.tool_registry.register_function("search_code_examples", "搜索代码示例和教程", search_code_examples)
+        self.tool_registry.register_function("verify_facts", "验证事实准确性", verify_facts)
+        print("▸ 搜索函数 wrappers 已注册")
+            
+    
+    def generate_content(
+        self,
+        node: ContentNode,
+        context: Dict[str, Any],
+        level: int,
+        additional_requirements: str = ""
+    ) -> Dict[str, Any]:
+        """
+        生成内容(使用 ReAct 模式)
+        
+        Args:
+            node: 当前节点
+            context: 写作上下文
+            level: 当前层级
+            additional_requirements: 额外要求
+            
+        Returns:
+            生成的内容数据
+        """
+        structure_requirements = get_structure_requirements(level)
+        word_count = get_word_count(level)
+        
+        # 构建写作任务描述(简化格式,参考示例代码)
+        task_description = f"""
+请撰写一篇技术专栏文章。
+
+层级: Level {level}/3
+话题: {node.title}
+描述: {node.description}
+要求字数: {word_count} 字(允许误差±10%)
+
+上下文信息:
+{json.dumps(context, ensure_ascii=False, indent=2)}
+
+结构要求:
+{structure_requirements}
+
+额外要求:
+{additional_requirements if additional_requirements else "无"}
+
+重要提示:
+- 完成写作后,必须使用 `\n\nFinish[JSON内容]` 格式输出结果
+- JSON 中的 `level` 字段必须是 {level}
+- `content` 字段必须包含完整的文章正文(Markdown格式)
+- 文章必须包含:引言、主体内容(3-5个小节)、实践案例、总结
+"""
+        
+        try:
+            response = self.agent.run(task_description)
+            
+            # 调试:打印真正的原始 LLM 响应(最后一次的响应)
+            print(f"\n{'='*70}")
+            print("▸ ReActAgent 原始 LLM 响应:")
+            print(f"{'='*70}")
+            if self.agent.last_raw_responses:
+                # 打印最后一次的原始响应(通常是包含 Finish[...] 的那次)
+                last_raw = self.agent.last_raw_responses[-1]
+                print(last_raw)
+                # print(last_raw[:2000] if len(last_raw) > 2000 else last_raw)
+                # if len(last_raw) > 2000:
+                    # print(f"\n... (响应过长,已截断,总长度: {len(last_raw)} 字符)")
+            else:
+                print("▸️  未捕获到原始响应")
+            print(f"{'='*70}\n")
+            
+            # 打印 run() 方法的返回值(通常是 final_answer)
+            print(f"▸ ReActAgent.run() 返回值:")
+            print(f"   {response[:500] if response and len(response) > 500 else response}")
+            print()
+            
+            # 检查响应是否有效
+            # 注意:即使 response 为空或错误,也要检查是否有原始响应可以提取
+            if not response or (isinstance(response, str) and not response.strip()):
+                print("▸️  ReActAgent 返回了空响应或空白响应")
+                print(f"   已收集的历史信息: {len(self.agent.last_history)} 条")
+                
+                # 尝试从最后一次原始响应中提取内容
+                if self.agent.last_raw_responses:
+                    last_raw = self.agent.last_raw_responses[-1]
+                    print(f"   尝试从最后一次原始响应中提取内容(长度: {len(last_raw)} 字符)...")
+                    # 尝试直接提取 JSON
+                    try:
+                        content_data = self._extract_json(last_raw)
+                        # 验证提取的 JSON 是否包含必需的字段
+                        if not isinstance(content_data, dict):
+                            raise ValueError("提取的内容不是字典格式")
+                        if 'content' not in content_data:
+                            print(f"   ▸️  提取的 JSON 缺少 'content' 字段")
+                            print(f"   可用字段: {list(content_data.keys())}")
+                            raise ValueError("提取的 JSON 缺少 'content' 字段")
+                        print("▸ 成功从原始响应中提取到内容")
+                        return content_data
+                    except Exception as e:
+                        print(f"   ▸️  从原始响应提取失败: {e}")
+                
+                # 如果提取失败,使用 fallback
+                return self._generate_content_with_history(
+                    node, context, level, structure_requirements, word_count,
+                    self.agent.last_history, task_description
+                )
+            
+            # 检查是否是错误消息
+            if "无法在限定步数内完成" in response or "抱歉" in response or "流程终止" in response:
+                print("▸️  ReActAgent 达到最大步数限制或无法完成任务")
+                print(f"   已收集的历史信息: {len(self.agent.last_history)} 条")
+                
+                # 即使返回错误消息,也尝试从最后一次原始响应中提取内容
+                if self.agent.last_raw_responses:
+                    last_raw = self.agent.last_raw_responses[-1]
+                    print(f"   尝试从最后一次原始响应中提取内容(长度: {len(last_raw)} 字符)...")
+                    try:
+                        content_data = self._extract_json(last_raw)
+                        # 验证提取的 JSON 是否包含必需的字段
+                        if not isinstance(content_data, dict):
+                            raise ValueError("提取的内容不是字典格式")
+                        if 'content' not in content_data:
+                            print(f"   ▸️  提取的 JSON 缺少 'content' 字段")
+                            print(f"   可用字段: {list(content_data.keys())}")
+                            raise ValueError("提取的 JSON 缺少 'content' 字段")
+                        print("▸ 成功从原始响应中提取到内容(尽管 ReActAgent 返回了错误消息)")
+                        return content_data
+                    except Exception as e:
+                        print(f"   ▸️  从原始响应提取失败: {e}")
+                
+                # 如果提取失败,基于历史信息生成内容
+                return self._generate_content_with_history(
+                    node, context, level, structure_requirements, word_count,
+                    self.agent.last_history, task_description
+                )
+            
+            # 如果 response 是 "JSON内容" 这样的占位符,从原始响应中提取
+            if response.strip() in ["JSON内容", "JSON", "内容"]:
+                print(f"▸️  ReActAgent 返回了占位符 '{response}',尝试从原始响应中提取...")
+                if self.agent.last_raw_responses:
+                    last_raw = self.agent.last_raw_responses[-1]
+                    print(f"   从最后一次原始响应中提取(长度: {len(last_raw)} 字符)...")
+                    try:
+                        content_data = self._extract_json(last_raw)
+                        if isinstance(content_data, dict) and 'content' in content_data:
+                            print("▸ 成功从原始响应中提取到内容")
+                            return content_data
+                    except Exception as e:
+                        print(f"   ▸️  从原始响应提取失败: {e}")
+            
+            content_data = self._extract_json(response)
+            
+            # 验证提取的 JSON 是否包含必需的字段
+            if not isinstance(content_data, dict):
+                raise ValueError(f"提取的内容不是字典格式: {type(content_data)}")
+            if 'content' not in content_data:
+                print(f"▸️  提取的 JSON 缺少 'content' 字段")
+                print(f"   可用字段: {list(content_data.keys())}")
+                print(f"   响应内容(前500字符): {response[:500]}")
+                
+                # 如果从 response 提取失败,尝试从原始响应中提取
+                if self.agent.last_raw_responses:
+                    last_raw = self.agent.last_raw_responses[-1]
+                    print(f"   尝试从最后一次原始响应中提取(长度: {len(last_raw)} 字符)...")
+                    try:
+                        content_data = self._extract_json(last_raw)
+                        if isinstance(content_data, dict) and 'content' in content_data:
+                            print("▸ 成功从原始响应中提取到内容")
+                            return content_data
+                    except Exception as e:
+                        print(f"   ▸️  从原始响应提取失败: {e}")
+                
+                raise ValueError("提取的 JSON 缺少 'content' 字段")
+            
+            return content_data
+        except Exception as e:
+            print(f"▸️  ReActAgent 执行失败: {e}")
+            import traceback
+            traceback.print_exc()
+            print(f"   已收集的历史信息: {len(self.agent.last_history)} 条")
+            print("   尝试基于历史信息生成内容...")
+            return self._generate_content_with_history(
+                node, context, level, structure_requirements, word_count,
+                self.agent.last_history, task_description
+            )
+    
+    def _generate_content_with_history(
+        self,
+        node: ContentNode,
+        context: Dict[str, Any],
+        level: int,
+        structure_requirements: str,
+        word_count: int,
+        history: List[str],
+        original_task: str
+    ) -> Dict[str, Any]:
+        """
+        当 ReActAgent 失败时,基于历史信息使用 SimpleAgent 生成内容
+        
+        Args:
+            history: ReActAgent 收集的历史信息(Thought、Action、Observation)
+        """
+        from hello_agents import SimpleAgent
+        
+        fallback_agent = SimpleAgent(
+            name="内容创作专家(备用)",
+            llm=self.llm,
+            system_prompt="你是一位专业的内容创作者,擅长撰写技术专栏文章。"
+        )
+        
+        # 构建包含历史信息的任务描述
+        history_summary = ""
+        if history:
+            history_summary = "\n\n## 已撰写的部分历史:\n"
+            for i, item in enumerate(history[-10:], 1):  # 只取最后10条历史
+                history_summary += f"{i}. {item}\n"
+            history_summary += "\n请基于以上信息继续完成写作任务。\n"
+        
+        task = f"""
+请撰写一篇技术专栏文章。
+
+话题: {node.title}
+描述: {node.description}
+要求字数: {word_count} 字
+
+结构要求:
+{structure_requirements}
+{history_summary}
+
+请直接输出 JSON 格式的内容:
+{{
+  "title": "{node.title}",
+  "level": {level},
+  "content": "完整的文章正文(markdown格式,包含引言、主体、案例、总结)",
+  "word_count": 实际字数,
+  "needs_expansion": false,
+  "subsections": [],
+  "metadata": {{}}
+}}
+"""
+        
+        print(f"▸ 使用 SimpleAgent 基于历史信息生成内容...")
+        response = fallback_agent.run(task)
+        return self._extract_json(response)
+    
+    def revise_content(
+        self,
+        original_content: str,
+        review_result: ReviewResult,
+        level: int
+    ) -> Dict[str, Any]:
+        """
+        根据评审意见修改内容
+        
+        Args:
+            original_content: 原始内容
+            review_result: 评审结果
+            level: 层级
+            
+        Returns:
+            修改后的内容数据
+        """
+        # 构建修改任务
+        task_description = f"""
+## 修改任务
+
+**原始内容**:
+{original_content[:500]}...
+
+**评审分数**: {review_result.score}/100
+**评审等级**: {review_result.grade}
+
+**主要问题**:
+{json.dumps(review_result.detailed_feedback.get('issues', [])[:3], ensure_ascii=False, indent=2)}
+
+**修改建议**:
+{json.dumps(review_result.revision_plan.get('priority_changes', []), ensure_ascii=False, indent=2)}
+
+请使用 ReAct 模式完成修改:
+1. 思考评审意见的核心要求
+2. 决定是否需要搜索新信息
+3. 修改内容
+4. 使用 Finish[修改后的JSON内容] 输出结果
+"""
+        
+        response = self.agent.run(task_description)
+        revised_data = self._extract_json(response)
+        
+        return revised_data
+    
+    def _extract_json(self, response: str) -> Dict[str, Any]:
+        """从响应中提取 JSON(使用统一的 JSONExtractor)"""
+        try:
+            return JSONExtractor.extract(
+                response,
+                required_fields=['content'],
+                fallback_fields={
+                    'subsections': [],
+                    'metadata': {},
+                    'needs_expansion': False
+                }
+            )
+        except Exception as e:
+            print(f"▸️  提取 JSON 时发生错误: {e}")
+            print(f"   响应内容(前1000字符): {response[:1000]}")
+            raise
+
+
+class ReviewerAgent:
+    """
+    评审 Agent - 使用 SimpleAgent 模式
+    
+    负责对生成的内容进行质量评审,提供详细的评分和修改建议
+    """
+    
+    def __init__(self):
+        from hello_agents import SimpleAgent
+        from prompts import get_reviewer_prompt
+        
+        self.llm = LLMService.get_llm()
+        self.reviewer_prompt = get_reviewer_prompt()
+        
+        self.agent = SimpleAgent(
+            name="内容评审专家",
+            llm=self.llm,
+            system_prompt="你是一位严格而专业的内容评审专家,擅长评估文章质量并提供建设性的修改意见。"
+        )
+    
+    def review_content(
+        self,
+        content: str,
+        level: int,
+        target_word_count: int,
+        key_points: List[str]
+    ) -> 'ReviewResult':
+        """
+        评审内容
+        
+        Args:
+            content: 待评审的内容
+            level: 内容层级
+            target_word_count: 目标字数
+            key_points: 关键要点
+            
+        Returns:
+            ReviewResult 实例
+        """
+        print(f"\n▸ ReviewerAgent 开始评审内容...")
+        print(f"   内容长度: {len(content)} 字符")
+        print(f"   目标字数: {target_word_count}")
+        
+        # 构建评审任务
+        task = self.reviewer_prompt.format(
+            level=level,
+            target_word_count=target_word_count,
+            key_points=json.dumps(key_points, ensure_ascii=False),
+            content=content
+        )
+        
+        response = self.agent.run(task)
+        review_data = self._extract_json(response)
+        
+        # 创建 ReviewResult 实例
+        result = ReviewResult.from_dict(review_data)
+        
+        print(f"▸ 评审完成")
+        print(f"   评分: {result.score}/100 ({result.grade})")
+        print(f"   需要修改: {'是' if result.needs_revision else '否'}")
+        
+        return result
+    
+    def _extract_json(self, response: str) -> Dict[str, Any]:
+        """从响应中提取 JSON"""
+        try:
+            return JSONExtractor.extract(
+                response,
+                required_fields=['score', 'grade'],
+                fallback_fields={
+                    'dimension_scores': {},
+                    'detailed_feedback': {'strengths': [], 'issues': []},
+                    'revision_plan': {'priority_changes': [], 'minor_improvements': []},
+                    'needs_revision': True,
+                    'estimated_revision_effort': '',
+                    'reviewer_notes': ''
+                }
+            )
+        except Exception as e:
+            print(f"▸️  评审结果解析失败: {e}")
+            # 返回默认的评审结果(需要修改)
+            return {
+                'score': 60,
+                'grade': '需改进',
+                'dimension_scores': {},
+                'detailed_feedback': {'strengths': [], 'issues': [{'problem': '评审结果解析失败'}]},
+                'revision_plan': {'priority_changes': [], 'minor_improvements': []},
+                'needs_revision': True,
+                'estimated_revision_effort': '未知',
+                'reviewer_notes': f'评审结果解析失败: {str(e)}'
+            }
+
+
+class RevisionAgent:
+    """
+    修改 Agent - 使用 SimpleAgent 模式
+    
+    根据评审意见修改内容
+    """
+    
+    def __init__(self):
+        from hello_agents import SimpleAgent
+        from prompts import get_revision_prompt
+        
+        self.llm = LLMService.get_llm()
+        self.revision_prompt = get_revision_prompt()
+        
+        self.agent = SimpleAgent(
+            name="内容修改专家",
+            llm=self.llm,
+            system_prompt="你是一位专业的内容创作者,擅长根据评审意见修改和优化文章。"
+        )
+    
+    def revise_content(
+        self,
+        original_content: str,
+        review_result: 'ReviewResult',
+        target_word_count: int
+    ) -> Dict[str, Any]:
+        """
+        根据评审意见修改内容
+        
+        Args:
+            original_content: 原始内容
+            review_result: 评审结果
+            target_word_count: 目标字数
+            
+        Returns:
+            修改后的内容数据
+        """
+        print(f"\n▸ RevisionAgent 开始修改内容...")
+        print(f"   原始评分: {review_result.score}/100")
+        
+        current_word_count = len(original_content)
+        word_count_min = int(target_word_count * 0.9)
+        word_count_max = int(target_word_count * 1.1)
+        
+        # 计算字数调整建议
+        if current_word_count < word_count_min:
+            word_count_adjustment = f"需要增加约 {word_count_min - current_word_count} 字"
+        elif current_word_count > word_count_max:
+            word_count_adjustment = f"需要删减约 {current_word_count - word_count_max} 字"
+        else:
+            word_count_adjustment = "字数在合理范围内"
+        
+        # 格式化评审信息
+        strengths = "\n".join([f"- {s}" for s in review_result.detailed_feedback.get('strengths', [])])
+        issues = "\n".join([
+            f"- [{issue.get('category', '未知')}] {issue.get('problem', '')}: {issue.get('suggestion', '')}"
+            for issue in review_result.detailed_feedback.get('issues', [])
+        ])
+        priority_changes = "\n".join([
+            f"- **{change.get('section', '')}**: {change.get('action', '')} - {change.get('detail', '')}"
+            for change in review_result.revision_plan.get('priority_changes', [])
+        ])
+        minor_improvements = "\n".join([
+            f"- {imp.get('section', '')}: {imp.get('detail', '')}"
+            for imp in review_result.revision_plan.get('minor_improvements', [])
+        ])
+        
+        # 构建修改任务
+        task = self.revision_prompt.format(
+            original_content=original_content,
+            score=review_result.score,
+            grade=review_result.grade,
+            strengths=strengths or "无",
+            issues=issues or "无",
+            reviewer_notes=review_result.reviewer_notes or "无",
+            priority_changes=priority_changes or "无",
+            minor_improvements=minor_improvements or "无",
+            word_count_range=f"{word_count_min}-{word_count_max}",
+            current_word_count=current_word_count,
+            word_count_adjustment=word_count_adjustment
+        )
+        
+        response = self.agent.run(task)
+        revised_data = self._extract_json(response)
+        
+        print(f"▸ 修改完成")
+        print(f"   修改后字数: {revised_data.get('word_count', len(revised_data.get('revised_content', '')))}")
+        
+        return revised_data
+    
+    def _extract_json(self, response: str) -> Dict[str, Any]:
+        """从响应中提取 JSON"""
+        try:
+            data = JSONExtractor.extract(
+                response,
+                required_fields=['revised_content'],
+                fallback_fields={
+                    'revision_summary': {'major_changes': [], 'minor_changes': [], 'preserved_strengths': []},
+                    'word_count': 0,
+                    'word_count_change': ''
+                }
+            )
+            # 如果没有 word_count,计算一下
+            if not data.get('word_count'):
+                data['word_count'] = len(data.get('revised_content', ''))
+            return data
+        except Exception as e:
+            print(f"▸️  修改结果解析失败: {e}")
+            raise
+
+
+class ReflectionWriterAgent:
+    """
+    反思写作 Agent - 使用 ReflectionAgent 模式
+    
+    ReflectionAgent 通过自我反思和迭代优化来改进输出,将评审和修改整合为一个 Agent:
+    1. 生成初稿
+    2. 自我评审(反思)
+    3. 根据反思修改(优化)
+    4. 达到质量标准
+    """
+    
+    def __init__(self):
+        self.llm = LLMService.get_llm()
+        
+        # 自定义 Reflection 提示词
+        reflection_prompts = {
+            "initial": """
+你是一位专业的内容创作者。请撰写以下内容的初稿:
+
+{task}
+
+请输出完整的 JSON 格式内容。
+""",
+            "reflect": """
+你是一位严格的内容评审专家。请评审以下内容:
+
+# 写作任务: {task}
+# 内容初稿: {content}
+
+请从以下维度评审:
+1. **内容质量** (40分): 准确性、完整性、深度、原创性
+2. **结构逻辑** (30分): 层次清晰、逻辑连贯、过渡自然
+3. **语言表达** (20分): 易读性、专业性、准确性
+4. **格式规范** (10分): 字数达标、格式正确、排版美观
+
+如果内容质量很好(85分以上),请回答"无需改进"。
+否则,请详细指出问题并提供具体的修改建议。
+""",
+            "refine": """
+请根据评审意见优化你的内容:
+
+# 原始任务: {task}
+# 当前内容: {last_attempt}
+# 评审意见: {feedback}
+
+请输出优化后的完整 JSON 格式内容。
+"""
+        }
+        
+        self.agent = ReflectionAgent(
+            name="反思写作专家",
+            llm=self.llm,
+            custom_prompts=reflection_prompts,
+            max_iterations=2  # 最多反思 2 次
+        )
+    
+    def generate_and_refine_content(
+        self,
+        node: ContentNode,
+        context: Dict[str, Any],
+        level: int
+    ) -> Dict[str, Any]:
+        """
+        生成并反思优化内容
+        
+        Args:
+            node: 当前节点
+            context: 写作上下文
+            level: 当前层级
+            
+        Returns:
+            优化后的内容数据
+        """
+        print(f"\n▸ ReflectionAgent 开始写作并自我反思...")
+        print(f"   使用模式: 初稿 → 自我评审 → 优化")
+        
+        structure_requirements = get_structure_requirements(level)
+        word_count = get_word_count(level)
+        
+        task_description = f"""
+## 写作任务
+
+**层级**: Level {level}/3
+**话题**: {node.title}
+**描述**: {node.description}
+**要求字数**: {word_count} 字(允许误差±10%)
+
+**结构要求**:
+{structure_requirements}
+
+**上下文**:
+{json.dumps(context, ensure_ascii=False, indent=2)}
+
+请输出完整的 JSON 格式内容:
+```json
+{{
+  "title": "章节标题",
+  "level": {level},
+  "content": "正文内容(markdown格式)",
+  "word_count": 实际字数,
+  "needs_expansion": true/false,
+  "subsections": [...],
+  "metadata": {{...}}
+}}
+```
+"""
+        
+        response = self.agent.run(task_description)
+        content_data = self._extract_json(response)
+        
+        print(f"▸ ReflectionAgent 完成反思优化")
+        
+        return content_data
+    
+    def _extract_json(self, response: str) -> Dict[str, Any]:
+        """从响应中提取 JSON(使用统一的 JSONExtractor)"""
+        try:
+            return JSONExtractor.extract(
+                response,
+                required_fields=['content'],
+                fallback_fields={
+                    'subsections': [],
+                    'metadata': {},
+                    'needs_expansion': False
+                }
+            )
+        except Exception as e:
+            print(f"▸️  JSON 解析失败: {e}")
+            raise
+

BIN
Co-creation-projects/melxy1997-ColumnWriter/assets/agent_run.jpg


Некоторые файлы не были показаны из-за большого количества измененных файлов