- **文件管理**: - 支持文件上传、解析和删除。 - 限制支持格式(txt, md, docx, pdf),最大 10MB。 - 提供前端上传区、文件列表及后端解析服务。 - **技能管理**: - 新增技能的创建、更新及删除功能。 - 支持分配自定义提示词和工具权限。 - 提供技能列表及分组工具选择交互界面。 同时调整相关 API 和前端组件,优化协作体验。
This commit is contained in:
@@ -6,13 +6,13 @@ from fastapi.responses import StreamingResponse
|
||||
from sqlmodel import Session, select, func
|
||||
|
||||
from ..database import get_session, engine
|
||||
from ..models import AIConfig, ChatMessage, Novel
|
||||
from ..models import AIConfig, ChatMessage, Novel, Skill
|
||||
from ..schemas import ChatRequest, ChatMessageRead, ChatMessageList
|
||||
from ..services.ai_provider import (
|
||||
stream_chat, simple_completion,
|
||||
build_assistant_message, build_tool_results_messages, ToolCall,
|
||||
)
|
||||
from ..services.tools import get_openai_tools, get_anthropic_tools, execute_tool
|
||||
from ..services.tools import get_openai_tools, get_anthropic_tools, execute_tool, get_tools_description
|
||||
|
||||
router = APIRouter(prefix="/api/chat", tags=["chat"])
|
||||
|
||||
@@ -38,6 +38,8 @@ TOOL_LABELS: dict[str, str] = {
|
||||
"create_chapter": "创建章节",
|
||||
"update_chapter": "更新章节",
|
||||
"delete_chapter": "删除章节",
|
||||
"list_files": "查询文件",
|
||||
"read_file": "读取文件",
|
||||
}
|
||||
|
||||
|
||||
@@ -46,13 +48,19 @@ def _build_system_prompt(
|
||||
session: Session,
|
||||
page_context: str | None = None,
|
||||
tools_enabled: bool = False,
|
||||
skill: Skill | None = None,
|
||||
allowed_tools: list[str] | None = None,
|
||||
) -> str:
|
||||
"""根据小说上下文和用户所在页面构建 system prompt"""
|
||||
"""根据小说上下文、Skill 和工具配置构建 system prompt"""
|
||||
parts = [
|
||||
"你是 Writing Red Dot 写作助手,专注于小说创作领域。",
|
||||
"你擅长:构思情节、塑造角色、打磨文笔、设计世界观、规划大纲结构。",
|
||||
]
|
||||
|
||||
# 注入 Skill 提示词
|
||||
if skill and skill.system_prompt:
|
||||
parts.append(f"\n## 当前技能:{skill.name}\n{skill.system_prompt}")
|
||||
|
||||
if novel_id:
|
||||
novel = session.get(Novel, novel_id)
|
||||
if novel:
|
||||
@@ -63,43 +71,20 @@ def _build_system_prompt(
|
||||
parts.append(f"\n用户当前正在查看:{page_context}。请结合当前页面场景提供针对性建议。")
|
||||
|
||||
if tools_enabled:
|
||||
parts.append(
|
||||
"\n## 你的工具能力\n"
|
||||
"你是一个具备工具调用能力的写作助手。你拥有以下专用工具,没有其他任何工具:\n"
|
||||
"\n### 小说信息\n"
|
||||
"1. get_novel_info — 查看当前小说的标题、简介等基础信息\n"
|
||||
"2. update_novel_info — 修改小说标题或简介\n"
|
||||
"\n### 大纲管理\n"
|
||||
"3. list_outlines — 列出所有大纲节点\n"
|
||||
"4. create_outline — 创建新的大纲节点(需要提供摘要,可选详情和父节点ID)\n"
|
||||
"5. update_outline — 更新指定大纲节点的摘要或详情\n"
|
||||
"6. delete_outline — 删除指定大纲节点\n"
|
||||
"\n### 世界观\n"
|
||||
"7. get_world_setting — 获取世界观设定内容\n"
|
||||
"8. save_world_setting — 保存或更新世界观设定\n"
|
||||
"\n### 角色管理\n"
|
||||
"9. list_characters — 列出所有角色\n"
|
||||
"10. create_character — 创建新角色(需要提供名称,可选描述)\n"
|
||||
"11. update_character — 更新指定角色的名称或描述\n"
|
||||
"12. delete_character — 删除指定角色\n"
|
||||
"\n### 章节管理\n"
|
||||
"13. list_chapters — 列出所有章节\n"
|
||||
"14. create_chapter — 创建新章节(需要提供标题,可选正文)\n"
|
||||
"15. update_chapter — 更新指定章节的标题或正文\n"
|
||||
"16. delete_chapter — 删除指定章节\n"
|
||||
"\n当用户询问你有哪些工具或能力时,请据实回答上述工具。"
|
||||
"\n当用户要求查看或修改小说信息、大纲、世界观、角色或章节时,请主动使用相应工具完成操作,并简要说明结果。"
|
||||
)
|
||||
# 动态生成工具能力描述(根据 allowed_tools 过滤)
|
||||
tools_desc = get_tools_description(allowed_tools)
|
||||
if tools_desc:
|
||||
parts.append(f"\n## 你的工具能力\n{tools_desc}")
|
||||
if not novel_id:
|
||||
parts.append("\n注意:工具操作需要关联到具体小说。如果用户需要使用工具,请提示他们先进入某本小说。")
|
||||
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def _get_tools(provider: str) -> list[dict]:
|
||||
def _get_tools(provider: str, allowed: list[str] | None = None) -> list[dict]:
|
||||
if provider == "anthropic":
|
||||
return get_anthropic_tools()
|
||||
return get_openai_tools()
|
||||
return get_anthropic_tools(allowed)
|
||||
return get_openai_tools(allowed)
|
||||
|
||||
|
||||
def _sse(data: dict) -> str:
|
||||
@@ -151,12 +136,26 @@ async def chat_stream(
|
||||
# 构建消息列表
|
||||
messages = [{"role": m.role, "content": m.content} for m in data.messages]
|
||||
|
||||
# 读取 Skill 配置
|
||||
skill: Skill | None = None
|
||||
allowed_tools: list[str] | None = None
|
||||
if data.skill_id:
|
||||
skill = session.get(Skill, data.skill_id)
|
||||
if skill and skill.allowed_tools:
|
||||
try:
|
||||
parsed = json.loads(skill.allowed_tools)
|
||||
if isinstance(parsed, list) and len(parsed) > 0:
|
||||
allowed_tools = parsed
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
|
||||
# 构建上下文
|
||||
use_tools = bool(data.tools_enabled)
|
||||
system_prompt = _build_system_prompt(
|
||||
data.novel_id, session, data.page_context, use_tools
|
||||
data.novel_id, session, data.page_context, use_tools,
|
||||
skill=skill, allowed_tools=allowed_tools,
|
||||
)
|
||||
tools = _get_tools(config.provider) if use_tools else None
|
||||
tools = _get_tools(config.provider, allowed_tools) if use_tools else None
|
||||
|
||||
async def generate():
|
||||
nonlocal messages
|
||||
|
||||
Reference in New Issue
Block a user