init
This commit is contained in:
commit
e5f66e0d24
80
.gitignore
vendored
Normal file
80
.gitignore
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
/dist/
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Cache files
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
# Test coverage
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Python cache files (since this project has a Python backend)
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# Python virtual environment
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
.env/
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# System files
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Ignore temporary files created by Office
|
||||||
|
~$*.doc
|
||||||
|
~$*.docx
|
||||||
|
~$*.xls
|
||||||
|
~$*.xlsx
|
||||||
|
~$*.ppt
|
||||||
|
~$*.pptx
|
||||||
|
|
||||||
|
/backend/logs/
|
||||||
|
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
/frontend/package-lock.json
|
||||||
|
.cursorignore
|
||||||
|
.cursor/
|
||||||
|
dev/
|
||||||
|
/frontend/.env.dev
|
||||||
|
|
||||||
|
docker-compose-local.yml
|
||||||
|
docker-compose-test.yml
|
||||||
|
/frontend/.env.wsl
|
||||||
|
/backend/config_local.py
|
||||||
|
|
||||||
|
.kiro/
|
||||||
|
logs/queue.log
|
||||||
|
test_feishu_login.py
|
||||||
|
|
||||||
|
jimeng_crawler/logs/
|
||||||
|
logs/app.log
|
||||||
|
/.vercel/project.json
|
||||||
|
/jimeng_crawler/jimeng_temp.py
|
||||||
|
|
||||||
|
.trae/
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/logs/
|
||||||
|
docker-compose.*.yml
|
||||||
4
agent/__init__.py
Normal file
4
agent/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# agent/__init__.py
|
||||||
|
"""
|
||||||
|
智能体定义
|
||||||
|
"""
|
||||||
65
agent/build_bible.py
Normal file
65
agent/build_bible.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"""
|
||||||
|
调度智能体 负责接收和分析用户的提示词,并调用智能调度其他智能体来处理工作
|
||||||
|
"""
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph
|
||||||
|
from langgraph.prebuilt import create_react_agent
|
||||||
|
from langgraph.graph.state import CompiledStateGraph
|
||||||
|
from utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
# 默认调度器列表
|
||||||
|
DefaultSchedulerList = []
|
||||||
|
|
||||||
|
# 默认代理提示词
|
||||||
|
DefaultAgentPrompt = f"""
|
||||||
|
# 角色 (Persona)
|
||||||
|
你不是一个普通的编剧,你是一位在短剧市场身经百战、爆款频出的**“顶级短剧改编专家”与“爆款操盘手”**。
|
||||||
|
你的核心人设与专长:
|
||||||
|
极致爽点制造机: 你对观众的“爽点”G点有着鬣狗般的嗅觉。你的天职就是找到、放大、并以最密集的节奏呈现“打脸”、“逆袭”、“揭秘”、“宠溺”等情节。
|
||||||
|
人物标签化大师: 你深知在短剧中,模糊等于无效。你擅长将人物的核心欲望和性格特点极致化、标签化,让观众在3秒内记住主角,5秒内恨上反派。
|
||||||
|
情绪过山车设计师: 你的剧本就像过山车。开篇即俯冲,5秒一反转,10秒一高潮,结尾必留下一个让人抓心挠肝的钩子。你为观众提供的是极致的情绪体验。
|
||||||
|
网络梗语言学家: 你的台词充满了网感和“梗”,既能推动剧情,又能引发观众的共鸣和吐槽欲。对话追求高信息密度,不说一句废话。
|
||||||
|
你的沟通风格:自信、犀利、直击要害,同时又能清晰地解释你每一个改编决策背后的商业逻辑和观众心理。
|
||||||
|
|
||||||
|
请严格按照下列JSON结构返回数据,不要有其他任何多余的信息和描述:
|
||||||
|
{{
|
||||||
|
"status": "当前阶段的状态",//取值范围在上述 status的描述中 不可写其他值
|
||||||
|
"reason":'',//失败原因 成功则为空字符串
|
||||||
|
"message":'',//回复给用户的内容
|
||||||
|
"node":'',//下一个节点名称
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_agent_prompt(prompt, SchedulerList):
|
||||||
|
"""创建代理提示词的辅助函数"""
|
||||||
|
if not SchedulerList or len(SchedulerList) == 0: return prompt
|
||||||
|
node_list = [f"{node.name}:{node.desc}" for node in SchedulerList]
|
||||||
|
return f"""
|
||||||
|
{prompt} \n
|
||||||
|
下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回:
|
||||||
|
{node_list} \n
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class BuildBibleAgent(CompiledStateGraph):
|
||||||
|
"""剧本圣经构建 智能体
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __new__(cls, llm=None, tools=[], SchedulerList=None):
|
||||||
|
"""创建并返回create_react_agent创建的对象"""
|
||||||
|
# 处理默认参数
|
||||||
|
if llm is None:
|
||||||
|
from tools.llm.huoshan_langchain import HuoshanChatModel
|
||||||
|
llm = HuoshanChatModel()
|
||||||
|
|
||||||
|
if SchedulerList is None:
|
||||||
|
SchedulerList = DefaultSchedulerList
|
||||||
|
|
||||||
|
# 创建并返回代理对象
|
||||||
|
return create_react_agent(
|
||||||
|
model=llm,
|
||||||
|
tools=tools,
|
||||||
|
prompt=create_agent_prompt(prompt=DefaultAgentPrompt, SchedulerList=SchedulerList),
|
||||||
|
)
|
||||||
65
agent/episode_create.py
Normal file
65
agent/episode_create.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"""
|
||||||
|
调度智能体 负责接收和分析用户的提示词,并调用智能调度其他智能体来处理工作
|
||||||
|
"""
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph
|
||||||
|
from langgraph.prebuilt import create_react_agent
|
||||||
|
from langgraph.graph.state import CompiledStateGraph
|
||||||
|
from utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
# 默认调度器列表
|
||||||
|
DefaultSchedulerList = []
|
||||||
|
|
||||||
|
# 默认代理提示词
|
||||||
|
DefaultAgentPrompt = f"""
|
||||||
|
# 角色 (Persona)
|
||||||
|
你不是一个普通的编剧,你是一位在短剧市场身经百战、爆款频出的**“顶级短剧改编专家”与“爆款操盘手”**。
|
||||||
|
你的核心人设与专长:
|
||||||
|
极致爽点制造机: 你对观众的“爽点”G点有着鬣狗般的嗅觉。你的天职就是找到、放大、并以最密集的节奏呈现“打脸”、“逆袭”、“揭秘”、“宠溺”等情节。
|
||||||
|
人物标签化大师: 你深知在短剧中,模糊等于无效。你擅长将人物的核心欲望和性格特点极致化、标签化,让观众在3秒内记住主角,5秒内恨上反派。
|
||||||
|
情绪过山车设计师: 你的剧本就像过山车。开篇即俯冲,5秒一反转,10秒一高潮,结尾必留下一个让人抓心挠肝的钩子。你为观众提供的是极致的情绪体验。
|
||||||
|
网络梗语言学家: 你的台词充满了网感和“梗”,既能推动剧情,又能引发观众的共鸣和吐槽欲。对话追求高信息密度,不说一句废话。
|
||||||
|
你的沟通风格:自信、犀利、直击要害,同时又能清晰地解释你每一个改编决策背后的商业逻辑和观众心理。
|
||||||
|
|
||||||
|
请严格按照下列JSON结构返回数据,不要有其他任何多余的信息和描述:
|
||||||
|
{{
|
||||||
|
"status": "当前阶段的状态",//取值范围在上述 status的描述中 不可写其他值
|
||||||
|
"reason":'',//失败原因 成功则为空字符串
|
||||||
|
"message":'',//回复给用户的内容
|
||||||
|
"node":'',//下一个节点名称
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_agent_prompt(prompt, SchedulerList):
|
||||||
|
"""创建代理提示词的辅助函数"""
|
||||||
|
if not SchedulerList or len(SchedulerList) == 0: return prompt
|
||||||
|
node_list = [f"{node.name}:{node.desc}" for node in SchedulerList]
|
||||||
|
return f"""
|
||||||
|
{prompt} \n
|
||||||
|
下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回:
|
||||||
|
{node_list} \n
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class EpisodeCreateAgent(CompiledStateGraph):
|
||||||
|
"""单集创作 智能体
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __new__(cls, llm=None, tools=[], SchedulerList=None):
|
||||||
|
"""创建并返回create_react_agent创建的对象"""
|
||||||
|
# 处理默认参数
|
||||||
|
if llm is None:
|
||||||
|
from tools.llm.huoshan_langchain import HuoshanChatModel
|
||||||
|
llm = HuoshanChatModel()
|
||||||
|
|
||||||
|
if SchedulerList is None:
|
||||||
|
SchedulerList = DefaultSchedulerList
|
||||||
|
|
||||||
|
# 创建并返回代理对象
|
||||||
|
return create_react_agent(
|
||||||
|
model=llm,
|
||||||
|
tools=tools,
|
||||||
|
prompt=create_agent_prompt(prompt=DefaultAgentPrompt, SchedulerList=SchedulerList),
|
||||||
|
)
|
||||||
136
agent/scheduler.py
Normal file
136
agent/scheduler.py
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
"""
|
||||||
|
调度智能体 负责接收和分析用户的提示词,并调用智能调度其他智能体来处理工作
|
||||||
|
"""
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph
|
||||||
|
from langgraph.prebuilt import create_react_agent
|
||||||
|
from langgraph.graph.state import CompiledStateGraph
|
||||||
|
from utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
# 默认调度器列表
|
||||||
|
DefaultSchedulerList = []
|
||||||
|
|
||||||
|
# 默认代理提示词
|
||||||
|
DefaultAgentPrompt = f"""
|
||||||
|
# 角色 (Persona)
|
||||||
|
你不是一个普通的编剧,你是一位在短剧市场身经百战、爆款频出的**“顶级短剧改编专家”与“爆款操盘手”**。
|
||||||
|
你的核心人设与专长:
|
||||||
|
极致爽点制造机: 你对观众的“爽点”G点有着鬣狗般的嗅觉。你的天职就是找到、放大、并以最密集的节奏呈现“打脸”、“逆袭”、“揭秘”、“宠溺”等情节。
|
||||||
|
人物标签化大师: 你深知在短剧中,模糊等于无效。你擅长将人物的核心欲望和性格特点极致化、标签化,让观众在3秒内记住主角,5秒内恨上反派。
|
||||||
|
情绪过山车设计师: 你的剧本就像过山车。开篇即俯冲,5秒一反转,10秒一高潮,结尾必留下一个让人抓心挠肝的钩子。你为观众提供的是极致的情绪体验。
|
||||||
|
网络梗语言学家: 你的台词充满了网感和“梗”,既能推动剧情,又能引发观众的共鸣和吐槽欲。对话追求高信息密度,不说一句废话。
|
||||||
|
你的沟通风格:自信、犀利、直击要害,同时又能清晰地解释你每一个改编决策背后的商业逻辑和观众心理。
|
||||||
|
|
||||||
|
# 任务总体步骤描述
|
||||||
|
1. 查找并确认原始剧本已就绪
|
||||||
|
2. 分析原始剧本得出`诊断与资产评估`,需要用户确认可以继续下一步,否则协助用户完成修改
|
||||||
|
3. 根据`诊断与资产评估`确定`改编思路`,需要用户确认可以继续下一步,否则协助用户完成修改
|
||||||
|
4. 根据`改编思路`生成`剧本圣经`,需要用户确认可以继续下一步,否则协助用户完成修改
|
||||||
|
5. 根据`改编思路`和`剧本圣经`持续创建单集创作,每创建2-3集后等待用户确认可继续,直至完成全部剧集。
|
||||||
|
6. 注意步骤具有上下级关系,且不能跳过。但是后续步骤可返回触发前面的任务:如生成单集到第3集后,用户提出要修改某个角色,此时应当返回第4步,并协助用户进行修改与确认;完成修改后重新执行第5步,即从第一集开始重新创作一遍;
|
||||||
|
|
||||||
|
# 智能体职责介绍
|
||||||
|
***调度智能体*** 名称:`scheduler` 描述:你自己,需要用户确认反馈时返回自身,并把状态设置成waiting;
|
||||||
|
***原始剧本分析 智能体*** 名称:`script_analysis` 描述:根据原始剧本分析并输出:`诊断与资产评估`;内容包括:故事内核诊断、可继承的宝贵资产(高光情节、神来之笔对白、独特人设闪光点)、以及核心问题与初步改编建议。用户需要对`诊断与资产评估`进行修改都直接交给该智能体;
|
||||||
|
***确立改编目标 智能体*** 名称:`strategic_planning` 描述:用户确认`诊断与资产评估`后,交给该智能体与用户深入沟通,明确改编的具体目标;输出:`改编思路`;此文件将作为所有后续改编的最高指导原则。用户需要对`改编思路`进行修改都直接交给该智能体;
|
||||||
|
***剧本圣经构建 智能体*** 名称:`build_bible` 描述:用户确认`改编思路`后,交给该智能体来构建`剧本圣经`,剧本圣经具体包括了这几个部分:核心大纲, 核心人物小传, 重大事件时间线, 总人物表; 用户需要对`剧本圣经`的几个组成部分[核心大纲, 核心人物小传, 重大事件时间线, 总人物表]进行修改都直接交给该智能体;
|
||||||
|
***单集创作 智能体*** 名称:`episode_create` 描述:用户确认`剧本圣经`后,交给该智能体来构建某一集的具体创作;注意该智能体仅负责单集的创作,因此该智能体的调度需要有你根据`剧本圣经`中的`核心大纲`来多次调用,逐步完成所有剧集的创作;对于某一集的具体修改直接交给该智能体;
|
||||||
|
|
||||||
|
***注意:智能体调用后最终会返回再次请求到你,你需要根据智能体的处理结果来决定下一步***
|
||||||
|
|
||||||
|
# 工具使用
|
||||||
|
上述智能体职责中提及的输出内容,都有对应的工具可供你调用进行查看;他们的查询工具名称分别对应如下:
|
||||||
|
原始剧本: `QueryOriginalScript`
|
||||||
|
诊断与资产评估: `QueryDiagnosisAndAssessment`
|
||||||
|
改编思路: `QueryAdaptationIdeas`
|
||||||
|
剧本圣经: `QueryScriptBible`
|
||||||
|
核心大纲: `QueryCoreOutline`
|
||||||
|
核心人物小传: `QueryCharacterProfile`
|
||||||
|
重大事件时间线: `QueryCoreEventTimeline`
|
||||||
|
总人物表: `QueryCharacterList`
|
||||||
|
单集完整内容: `QuerySingleEpisodeContent`
|
||||||
|
未完成的集数: `QueryUnfinishedEpisodeCount`
|
||||||
|
已完成的集数: `QueryCompletedEpisodeCount`
|
||||||
|
|
||||||
|
***每次用户的输入都会携带当前`总任务的进度与任务状态`,注意查看并分析是否应该回复用户等待或提醒用户确认继续下一步***
|
||||||
|
# 总任务的进度与任务状态数据结构为 {{"step": "waiting_script", "status": "running", "from_type":"user", "reason": "waiting_script", "retry_count": 0}}
|
||||||
|
|
||||||
|
step: 阶段名称
|
||||||
|
wait_for_input: 等待用户提供原始剧本
|
||||||
|
script_analysis: 原始剧本分析
|
||||||
|
strategic_planning: 确立改编目标
|
||||||
|
build_bible: 剧本圣经构建
|
||||||
|
episode_create_loop: 剧集创作
|
||||||
|
finish: 所有剧集创作完成
|
||||||
|
|
||||||
|
status: 当前阶段的状态
|
||||||
|
waiting: 等待用户反馈、确认
|
||||||
|
running: 进行中
|
||||||
|
failed: 失败
|
||||||
|
completed: 完成
|
||||||
|
|
||||||
|
"from_type": 本次请求来着哪里
|
||||||
|
user: 用户
|
||||||
|
agent: 智能体返回
|
||||||
|
|
||||||
|
"reason": 失败原因,仅在`status`为`failed`时返回
|
||||||
|
字符串内容
|
||||||
|
|
||||||
|
"retry_count": 重试次数
|
||||||
|
|
||||||
|
# 职责
|
||||||
|
分析用户输入与`总任务的进度与任务状态`,以下是几种情况的示例:
|
||||||
|
1 `wait_for_input` 向用户问好,并介绍你作为“爆款短剧操盘手”的身份和专业工作流程,礼貌地请用户提供需要改编的原始剧本。如果用户没有提供原始剧本,你将持续友好地提醒,此时状态始终为waiting,直到获取原始剧本为止。
|
||||||
|
2 `script_analysis` 读取到原始剧本并从输入中分析出可以继续后进入,调用`原始剧本分析 智能体`继续后续工作;running时,礼貌回复用户并提醒用户任务真正进行中;completed代表任务完成,此时可等待用户反馈;直到跟用户确认可以进行下一步后再继续后续任务;
|
||||||
|
3 `strategic_planning` 根据`诊断与资产评估`的结果,调用`确立改编目标 智能体`,并返回结果。
|
||||||
|
4 `build_bible` 根据`改编思路`的结果,调用`剧本圣经构建 智能体`,并返回结果。
|
||||||
|
5 `episode_create_loop` 根据`剧本圣经`的结果,调用`单集创作 智能体
|
||||||
|
5 `finish` 所有剧集完成后设置为该状态,但是不要返回node==end_node,因为用户还可以继续输入来进一步修改产出内容;
|
||||||
|
|
||||||
|
***当任意一个智能体返回失败时,你需要分析reason字段中的内容,来决定是否进行重试,如果需要重试则给retry_count加1,并交给失败的那个智能体重试一次;如果retry_count超过了3次,或者失败原因不适合重试则反馈给用户说任务失败了,请稍后再试***
|
||||||
|
|
||||||
|
请严格按照下列JSON结构返回数据,不要有其他任何多余的信息和描述:
|
||||||
|
{{
|
||||||
|
"step": "阶段名称",//取值范围在上述 step的描述中 不可写其他值
|
||||||
|
"status": "当前阶段的状态",//取值范围在上述 status的描述中 不可写其他值
|
||||||
|
"agent":'',//分析后得出由哪个智能体继续任务,此处为智能体名称;如果需要继续与用户交互或仅需要回复用户则为空字符串
|
||||||
|
"message":'',//回复给用户的内容
|
||||||
|
"node":'',//下一个节点名称
|
||||||
|
}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_agent_prompt(prompt, SchedulerList):
|
||||||
|
"""创建代理提示词的辅助函数"""
|
||||||
|
if not SchedulerList or len(SchedulerList) == 0: return prompt
|
||||||
|
node_list = [f"{node.name}:{node.desc}" for node in SchedulerList]
|
||||||
|
return f"""
|
||||||
|
{prompt} \n
|
||||||
|
下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回:
|
||||||
|
{node_list} \n
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class SchedulerAgent(CompiledStateGraph):
|
||||||
|
"""智能调度智能体类
|
||||||
|
|
||||||
|
该类负责接收用户的提示词,并调用其他智能体来处理工作。
|
||||||
|
"""
|
||||||
|
def __new__(cls, llm=None, tools=[], SchedulerList=None):
|
||||||
|
"""创建并返回create_react_agent创建的对象"""
|
||||||
|
# 处理默认参数
|
||||||
|
if llm is None:
|
||||||
|
from tools.llm.huoshan_langchain import HuoshanChatModel
|
||||||
|
llm = HuoshanChatModel()
|
||||||
|
|
||||||
|
if SchedulerList is None:
|
||||||
|
SchedulerList = DefaultSchedulerList
|
||||||
|
|
||||||
|
# 创建并返回代理对象
|
||||||
|
return create_react_agent(
|
||||||
|
model=llm,
|
||||||
|
tools=tools,
|
||||||
|
prompt=create_agent_prompt(prompt=DefaultAgentPrompt, SchedulerList=SchedulerList),
|
||||||
|
)
|
||||||
65
agent/script_analysis.py
Normal file
65
agent/script_analysis.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"""
|
||||||
|
调度智能体 负责接收和分析用户的提示词,并调用智能调度其他智能体来处理工作
|
||||||
|
"""
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph
|
||||||
|
from langgraph.prebuilt import create_react_agent
|
||||||
|
from langgraph.graph.state import CompiledStateGraph
|
||||||
|
from utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
# 默认调度器列表
|
||||||
|
DefaultSchedulerList = []
|
||||||
|
|
||||||
|
# 默认代理提示词
|
||||||
|
DefaultAgentPrompt = f"""
|
||||||
|
# 角色 (Persona)
|
||||||
|
你不是一个普通的编剧,你是一位在短剧市场身经百战、爆款频出的**“顶级短剧改编专家”与“爆款操盘手”**。
|
||||||
|
你的核心人设与专长:
|
||||||
|
极致爽点制造机: 你对观众的“爽点”G点有着鬣狗般的嗅觉。你的天职就是找到、放大、并以最密集的节奏呈现“打脸”、“逆袭”、“揭秘”、“宠溺”等情节。
|
||||||
|
人物标签化大师: 你深知在短剧中,模糊等于无效。你擅长将人物的核心欲望和性格特点极致化、标签化,让观众在3秒内记住主角,5秒内恨上反派。
|
||||||
|
情绪过山车设计师: 你的剧本就像过山车。开篇即俯冲,5秒一反转,10秒一高潮,结尾必留下一个让人抓心挠肝的钩子。你为观众提供的是极致的情绪体验。
|
||||||
|
网络梗语言学家: 你的台词充满了网感和“梗”,既能推动剧情,又能引发观众的共鸣和吐槽欲。对话追求高信息密度,不说一句废话。
|
||||||
|
你的沟通风格:自信、犀利、直击要害,同时又能清晰地解释你每一个改编决策背后的商业逻辑和观众心理。
|
||||||
|
|
||||||
|
请严格按照下列JSON结构返回数据,不要有其他任何多余的信息和描述:
|
||||||
|
{{
|
||||||
|
"status": "当前阶段的状态",//取值范围在上述 status的描述中 不可写其他值
|
||||||
|
"reason":'',//失败原因 成功则为空字符串
|
||||||
|
"message":'',//回复给用户的内容
|
||||||
|
"node":'',//下一个节点名称
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_agent_prompt(prompt, SchedulerList):
|
||||||
|
"""创建代理提示词的辅助函数"""
|
||||||
|
if not SchedulerList or len(SchedulerList) == 0: return prompt
|
||||||
|
node_list = [f"{node.name}:{node.desc}" for node in SchedulerList]
|
||||||
|
return f"""
|
||||||
|
{prompt} \n
|
||||||
|
下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回:
|
||||||
|
{node_list} \n
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ScriptAnalysisAgent(CompiledStateGraph):
|
||||||
|
"""原始剧本分析 智能体
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __new__(cls, llm=None, tools=[], SchedulerList=None):
|
||||||
|
"""创建并返回create_react_agent创建的对象"""
|
||||||
|
# 处理默认参数
|
||||||
|
if llm is None:
|
||||||
|
from tools.llm.huoshan_langchain import HuoshanChatModel
|
||||||
|
llm = HuoshanChatModel()
|
||||||
|
|
||||||
|
if SchedulerList is None:
|
||||||
|
SchedulerList = DefaultSchedulerList
|
||||||
|
|
||||||
|
# 创建并返回代理对象
|
||||||
|
return create_react_agent(
|
||||||
|
model=llm,
|
||||||
|
tools=tools,
|
||||||
|
prompt=create_agent_prompt(prompt=DefaultAgentPrompt, SchedulerList=SchedulerList),
|
||||||
|
)
|
||||||
65
agent/strategic_planning.py
Normal file
65
agent/strategic_planning.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"""
|
||||||
|
调度智能体 负责接收和分析用户的提示词,并调用智能调度其他智能体来处理工作
|
||||||
|
"""
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph
|
||||||
|
from langgraph.prebuilt import create_react_agent
|
||||||
|
from langgraph.graph.state import CompiledStateGraph
|
||||||
|
from utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
# 默认调度器列表
|
||||||
|
DefaultSchedulerList = []
|
||||||
|
|
||||||
|
# 默认代理提示词
|
||||||
|
DefaultAgentPrompt = f"""
|
||||||
|
# 角色 (Persona)
|
||||||
|
你不是一个普通的编剧,你是一位在短剧市场身经百战、爆款频出的**“顶级短剧改编专家”与“爆款操盘手”**。
|
||||||
|
你的核心人设与专长:
|
||||||
|
极致爽点制造机: 你对观众的“爽点”G点有着鬣狗般的嗅觉。你的天职就是找到、放大、并以最密集的节奏呈现“打脸”、“逆袭”、“揭秘”、“宠溺”等情节。
|
||||||
|
人物标签化大师: 你深知在短剧中,模糊等于无效。你擅长将人物的核心欲望和性格特点极致化、标签化,让观众在3秒内记住主角,5秒内恨上反派。
|
||||||
|
情绪过山车设计师: 你的剧本就像过山车。开篇即俯冲,5秒一反转,10秒一高潮,结尾必留下一个让人抓心挠肝的钩子。你为观众提供的是极致的情绪体验。
|
||||||
|
网络梗语言学家: 你的台词充满了网感和“梗”,既能推动剧情,又能引发观众的共鸣和吐槽欲。对话追求高信息密度,不说一句废话。
|
||||||
|
你的沟通风格:自信、犀利、直击要害,同时又能清晰地解释你每一个改编决策背后的商业逻辑和观众心理。
|
||||||
|
|
||||||
|
请严格按照下列JSON结构返回数据,不要有其他任何多余的信息和描述:
|
||||||
|
{{
|
||||||
|
"status": "当前阶段的状态",//取值范围在上述 status的描述中 不可写其他值
|
||||||
|
"reason":'',//失败原因 成功则为空字符串
|
||||||
|
"message":'',//回复给用户的内容
|
||||||
|
"node":'',//下一个节点名称
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_agent_prompt(prompt, SchedulerList):
|
||||||
|
"""创建代理提示词的辅助函数"""
|
||||||
|
if not SchedulerList or len(SchedulerList) == 0: return prompt
|
||||||
|
node_list = [f"{node.name}:{node.desc}" for node in SchedulerList]
|
||||||
|
return f"""
|
||||||
|
{prompt} \n
|
||||||
|
下面返回数据中node字段的取值范围列表([{{名称:描述}}]),请根据你的分析结果选择一个节点名称返回:
|
||||||
|
{node_list} \n
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class StrategicPlanningAgent(CompiledStateGraph):
|
||||||
|
"""确立改编目标 智能体
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __new__(cls, llm=None, tools=[], SchedulerList=None):
|
||||||
|
"""创建并返回create_react_agent创建的对象"""
|
||||||
|
# 处理默认参数
|
||||||
|
if llm is None:
|
||||||
|
from tools.llm.huoshan_langchain import HuoshanChatModel
|
||||||
|
llm = HuoshanChatModel()
|
||||||
|
|
||||||
|
if SchedulerList is None:
|
||||||
|
SchedulerList = DefaultSchedulerList
|
||||||
|
|
||||||
|
# 创建并返回代理对象
|
||||||
|
return create_react_agent(
|
||||||
|
model=llm,
|
||||||
|
tools=tools,
|
||||||
|
prompt=create_agent_prompt(prompt=DefaultAgentPrompt, SchedulerList=SchedulerList),
|
||||||
|
)
|
||||||
4
api/__init__.py
Normal file
4
api/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# api/__init__.py
|
||||||
|
"""
|
||||||
|
第三方服务API接入模块
|
||||||
|
"""
|
||||||
1354
api/huoshan.py
Normal file
1354
api/huoshan.py
Normal file
File diff suppressed because it is too large
Load Diff
40
app.py
Normal file
40
app.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from flask import Flask
|
||||||
|
from flask_cors import CORS
|
||||||
|
from routes.langgraph_routes import bp as langgraph_bp
|
||||||
|
from config.web_config import WEB_CONFIG
|
||||||
|
|
||||||
|
def create_app():
|
||||||
|
"""应用工厂函数"""
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# 应用配置
|
||||||
|
app.config['SECRET_KEY'] = WEB_CONFIG['secret_key']
|
||||||
|
app.config['SESSION_TYPE'] = WEB_CONFIG['session_type']
|
||||||
|
app.config['SESSION_PERMANENT'] = WEB_CONFIG['session_permanent']
|
||||||
|
app.config['SESSION_USE_SIGNER'] = WEB_CONFIG['session_use_signer']
|
||||||
|
app.config['SESSION_KEY_PREFIX'] = WEB_CONFIG['session_key_prefix']
|
||||||
|
app.config['MAX_CONTENT_LENGTH'] = WEB_CONFIG['max_content_length']
|
||||||
|
|
||||||
|
# 初始化CORS
|
||||||
|
CORS(app,
|
||||||
|
origins=WEB_CONFIG['cors_origins'],
|
||||||
|
methods=WEB_CONFIG['cors_methods'],
|
||||||
|
headers=WEB_CONFIG['cors_headers'])
|
||||||
|
|
||||||
|
# 注册蓝图
|
||||||
|
app.register_blueprint(langgraph_bp, url_prefix=WEB_CONFIG['api_prefix'])
|
||||||
|
|
||||||
|
@app.route('/health')
|
||||||
|
def health_check():
|
||||||
|
"""健康检查端点"""
|
||||||
|
return {'status': 'healthy', 'message': 'LangGraph Web Service is running'}
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = create_app()
|
||||||
|
app.run(
|
||||||
|
host=WEB_CONFIG['host'],
|
||||||
|
port=WEB_CONFIG['port'],
|
||||||
|
debug=WEB_CONFIG['debug']
|
||||||
|
)
|
||||||
8
config/__init__.py
Normal file
8
config/__init__.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# 导入包内的其他模块
|
||||||
|
# 使用相对导入确保在各种运行环境下都能正常工作
|
||||||
|
from .base_config import *
|
||||||
|
from .api_config import *
|
||||||
|
from .web_config import *
|
||||||
|
|
||||||
|
# 定义包级别的变量
|
||||||
|
PACKAGE_VERSION = "1.0.0"
|
||||||
83
config/api_config.py
Normal file
83
config/api_config.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# API配置
|
||||||
|
API_CONFIG = {
|
||||||
|
'OSS_HOST': 'oss.xintiao85.com',
|
||||||
|
'OSS_BUCKET_NAME': 'km1',
|
||||||
|
# 'deepseek_ali': {
|
||||||
|
# 'OPENAI_API_KEY': 'sk-7b5b52171ba44436960e09f9440327dc',
|
||||||
|
# 'OPENAI_BASE_URL': 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||||
|
# 'CHAT_MODEL': 'deepseek-v3',
|
||||||
|
# 'REASONING_MODEL': 'deepseek-r1'
|
||||||
|
# },
|
||||||
|
'deepseek': {
|
||||||
|
'OPENAI_API_KEY': 'sk-571923fdcb0e493d8def7e2d78c02cb8',
|
||||||
|
'OPENAI_BASE_URL': 'https://api.deepseek.com',
|
||||||
|
'CHAT_MODEL': 'deepseek-chat',
|
||||||
|
'REASONING_MODEL': 'deepseek-reasoner'
|
||||||
|
},
|
||||||
|
'comfyui_image': {
|
||||||
|
'base_url': "https://u552342-991f-d12bf5ae.nmb1.seetacloud.com:8443"
|
||||||
|
},
|
||||||
|
'comfyui_video': {
|
||||||
|
'base_url': 'https://u552342-ba69-b66614c2.bjc1.seetacloud.com:8443'
|
||||||
|
},
|
||||||
|
'vidu': {
|
||||||
|
'api_key': 'vda_2710003370387436_qHyxyFYfd3DRfPSMJzGyxABChzUcY9Cg',
|
||||||
|
'base_url': 'https://api.vidu.cn/ent/v2'
|
||||||
|
},
|
||||||
|
'jimeng_video': {
|
||||||
|
'AccessKeyId': 'AKLTZmJkZjAxY2M0ZGVlNGJkMjg2YTcxODE2N2E2MDkwNTI',
|
||||||
|
'SecretAccessKey': 'TURNMk1EZ3pZak0zWWpVeU5HUTRObUV3WlRJNE1XUmpNbVJoTVdVNE0yWQ=='
|
||||||
|
},
|
||||||
|
'huoshan': {
|
||||||
|
'ark_api_key': '0d5189b3-9a03-4393-81be-8c1ba1e97cbb',
|
||||||
|
'AccessKey': 'AKLTYjQyYmE1ZDAwZTY5NGZiOWI3ODZkZDhhOWE4MzVjODE',
|
||||||
|
'SecretKey': 'WlRKa05EbGhZVEUyTXpjNU5ESmpPRGt5T0RJNFl6QmhPR0pqTVRjMVpUWQ==',
|
||||||
|
'model':{
|
||||||
|
'jimeng_image3.0':'doubao-seedream-3-0-t2i-250415',
|
||||||
|
'jimeng_video3.0':'doubao-seedance-1-0-lite-i2v-250428',
|
||||||
|
'jimeng_video3.0_t2v': 'doubao-seedance-1-0-lite-t2v-250428', # 即梦lite 文生视频
|
||||||
|
'jimeng_video3.0_pro':'doubao-seedance-1-0-pro-250528',
|
||||||
|
'doubao_seed_1.6':'doubao-seed-1.6-250615',
|
||||||
|
},
|
||||||
|
# 工作流 空间名
|
||||||
|
'workflow_space_name':"kemeng",
|
||||||
|
# 文件路径
|
||||||
|
'workflow_file_path':"workflow",
|
||||||
|
# 工作流id
|
||||||
|
'workflow_id':{
|
||||||
|
'video_frame_upsample': '282c8a5e105e44348f3c38a2d9ccf97e',
|
||||||
|
'video_super_resolution': 'a294a07f67d54ca69ab9eb0f6466a5b5',
|
||||||
|
},
|
||||||
|
# 豆包语音 大模型 语音合成
|
||||||
|
'doubao_voice':{
|
||||||
|
#实例ID
|
||||||
|
'InstanceID':'BigTTS7428139522913931561',
|
||||||
|
'Cluster ID':'volcano_tts',
|
||||||
|
'resourceID':'volc.service_type.10029',# 语音合成大模型-字符版
|
||||||
|
'AppID':'3821149055',
|
||||||
|
'AccessToken':'vQ9_m1-umD1mTwcW3HziEGN96dIX-Ej_',
|
||||||
|
'SecretKey':'cCsZg2IlytxlpgKQzQYuwC5ZcDRWKspV',
|
||||||
|
},
|
||||||
|
# 火山 豆包语音 声音复刻大模型
|
||||||
|
'doubao_voice_clone':{
|
||||||
|
# 'InstanceID':'BigTTS7428139522913931561',
|
||||||
|
# 'Cluster ID':'volcano_tts',
|
||||||
|
# 'resourceID':'volc.service_type.10029',# 语音合成大模型-字符版
|
||||||
|
'AppID':'3821149055',
|
||||||
|
'AccessToken':'vQ9_m1-umD1mTwcW3HziEGN96dIX-Ej_',
|
||||||
|
'SecretKey':'cCsZg2IlytxlpgKQzQYuwC5ZcDRWKspV',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'jimeng_free_api': {
|
||||||
|
'base_url': 'http://127.0.0.1:3300',
|
||||||
|
'token': ''
|
||||||
|
},
|
||||||
|
'minimaxi': {
|
||||||
|
'group_id':'1945039416510649202',
|
||||||
|
'api_key': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiLkuLrlrp4o5p2t5beeKeS6kuWKqOenkeaKgOaciemZkOWFrOWPuCIsIlVzZXJOYW1lIjoi5rKI6bmP6aOeIiwiQWNjb3VudCI6IiIsIlN1YmplY3RJRCI6IjE5NDUwMzk0MTY1MTkwMzc4MTAiLCJQaG9uZSI6IjEzNjExMTcyNDcxIiwiR3JvdXBJRCI6IjE5NDUwMzk0MTY1MTA2NDkyMDIiLCJQYWdlTmFtZSI6IiIsIk1haWwiOiIiLCJDcmVhdGVUaW1lIjoiMjAyNS0wNy0xNiAxOTo0OToyNyIsIlRva2VuVHlwZSI6MSwiaXNzIjoibWluaW1heCJ9.qrLtNgZd425bJNkgVzTCA_OdIXdqvx5uPkTOF_C8Cfzs9a5yO2S1LOJoQT6-bpPgATKisWce6Z6rsfjxNT2EHz5jPKel0Vl3kUvNPNUrUIJwG5uR_ewkJnkx8iplLY_3yB1BCJPybvvRj9oyy-KB0gv6d3Pmqe_OVyCOAQvLTkCeT5X7OnRSHAOfevEwYxRlrfI6aPrJw2SNaJ2eNkeO-uwf1i_hE84H5xPrpi9Gpb9KMWMk5PtUgz6enZ-WqWlrSZuu6VVbKo3JFkKGT2iDc5DqIj9O78oSm-brWLCQkOsKOYOJmNo8di5KlhllX_Gw8y1jZDPFu7A-xXUCw7unYQ',
|
||||||
|
'callback_url':'',
|
||||||
|
},
|
||||||
|
'pai': {
|
||||||
|
'api_key': 'sk-e88a776d7b146779a39ce86783042e3d',
|
||||||
|
}
|
||||||
|
}
|
||||||
7
config/base_config.py
Normal file
7
config/base_config.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
# MongoDB 连接配置
|
||||||
|
MONGO_URI = "mongodb://localhost:27017"
|
||||||
|
MONGO_MAIN_DB_NAME = "mm"
|
||||||
|
MONGO_CHECKPOINT_DB_NAME = "agent_writer_checkpoints"
|
||||||
|
|
||||||
|
|
||||||
88
config/web_config.py
Normal file
88
config/web_config.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# config/web_config.py
|
||||||
|
"""
|
||||||
|
Web服务配置文件
|
||||||
|
包含端口号、会话配置等相关设置
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Flask应用配置
|
||||||
|
class WebConfig:
|
||||||
|
# 服务端口
|
||||||
|
PORT = int(os.environ.get('WEB_PORT', 3400))
|
||||||
|
|
||||||
|
# 服务主机
|
||||||
|
HOST = os.environ.get('WEB_HOST', '0.0.0.0')
|
||||||
|
|
||||||
|
# 调试模式
|
||||||
|
DEBUG = os.environ.get('WEB_DEBUG', 'False').lower() == 'true'
|
||||||
|
|
||||||
|
# 密钥用于会话和CSRF保护
|
||||||
|
SECRET_KEY = os.environ.get('WEB_SECRET_KEY', 'your-secret-key-change-in-production')
|
||||||
|
|
||||||
|
# 会话配置
|
||||||
|
SESSION_TYPE = os.environ.get('SESSION_TYPE', 'filesystem') # 可选: filesystem, mongodb, redis
|
||||||
|
SESSION_PERMANENT = os.environ.get('SESSION_PERMANENT', 'False').lower() == 'true'
|
||||||
|
SESSION_USE_SIGNER = os.environ.get('SESSION_USE_SIGNER', 'True').lower() == 'true'
|
||||||
|
SESSION_KEY_PREFIX = os.environ.get('SESSION_KEY_PREFIX', 'langgraph_session:')
|
||||||
|
|
||||||
|
# 数据库会话配置(如果使用数据库存储会话)
|
||||||
|
SESSION_MONGODB_DB = os.environ.get('SESSION_MONGODB_DB', 'langgraph_sessions')
|
||||||
|
SESSION_MONGODB_COLLECT = os.environ.get('SESSION_MONGODB_COLLECT', 'sessions')
|
||||||
|
|
||||||
|
# API配置
|
||||||
|
API_VERSION = 'v1'
|
||||||
|
API_PREFIX = f'/api/{API_VERSION}'
|
||||||
|
|
||||||
|
# CORS配置
|
||||||
|
CORS_ORIGINS = os.environ.get('CORS_ORIGINS', '*').split(',')
|
||||||
|
CORS_METHODS = os.environ.get('CORS_METHODS', 'GET,POST,PUT,DELETE,OPTIONS').split(',')
|
||||||
|
CORS_HEADERS = os.environ.get('CORS_HEADERS', 'Content-Type,Authorization').split(',')
|
||||||
|
|
||||||
|
# 请求体大小限制
|
||||||
|
MAX_CONTENT_LENGTH = int(os.environ.get('MAX_CONTENT_LENGTH', 16 * 1024 * 1024)) # 默认16MB
|
||||||
|
|
||||||
|
# 上传文件配置
|
||||||
|
UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER', 'uploads')
|
||||||
|
MAX_UPLOAD_SIZE = int(os.environ.get('MAX_UPLOAD_SIZE', 50 * 1024 * 1024)) # 默认50MB
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
|
||||||
|
LOG_FORMAT = os.environ.get('LOG_FORMAT', '[%(asctime)s] %(levelname)s in %(module)s: %(message)s')
|
||||||
|
|
||||||
|
# 安全配置
|
||||||
|
# 是否启用HTTPS
|
||||||
|
HTTPS_ENABLED = os.environ.get('HTTPS_ENABLED', 'False').lower() == 'true'
|
||||||
|
|
||||||
|
# JWT配置(如果使用JWT认证)
|
||||||
|
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY', 'jwt-secret-string')
|
||||||
|
JWT_ACCESS_TOKEN_EXPIRES = int(os.environ.get('JWT_ACCESS_TOKEN_EXPIRES', 3600)) # 默认1小时
|
||||||
|
JWT_REFRESH_TOKEN_EXPIRES = int(os.environ.get('JWT_REFRESH_TOKEN_EXPIRES', 86400)) # 默认24小时
|
||||||
|
|
||||||
|
# 配置实例
|
||||||
|
WEB_CONFIG = {
|
||||||
|
'port': WebConfig.PORT,
|
||||||
|
'host': WebConfig.HOST,
|
||||||
|
'debug': WebConfig.DEBUG,
|
||||||
|
'secret_key': WebConfig.SECRET_KEY,
|
||||||
|
'session_type': WebConfig.SESSION_TYPE,
|
||||||
|
'session_permanent': WebConfig.SESSION_PERMANENT,
|
||||||
|
'session_use_signer': WebConfig.SESSION_USE_SIGNER,
|
||||||
|
'session_key_prefix': WebConfig.SESSION_KEY_PREFIX,
|
||||||
|
'session_mongodb_db': WebConfig.SESSION_MONGODB_DB,
|
||||||
|
'session_mongodb_collect': WebConfig.SESSION_MONGODB_COLLECT,
|
||||||
|
'api_version': WebConfig.API_VERSION,
|
||||||
|
'api_prefix': WebConfig.API_PREFIX,
|
||||||
|
'cors_origins': WebConfig.CORS_ORIGINS,
|
||||||
|
'cors_methods': WebConfig.CORS_METHODS,
|
||||||
|
'cors_headers': WebConfig.CORS_HEADERS,
|
||||||
|
'max_content_length': WebConfig.MAX_CONTENT_LENGTH,
|
||||||
|
'upload_folder': WebConfig.UPLOAD_FOLDER,
|
||||||
|
'max_upload_size': WebConfig.MAX_UPLOAD_SIZE,
|
||||||
|
'log_level': WebConfig.LOG_LEVEL,
|
||||||
|
'log_format': WebConfig.LOG_FORMAT,
|
||||||
|
'https_enabled': WebConfig.HTTPS_ENABLED,
|
||||||
|
'jwt_secret_key': WebConfig.JWT_SECRET_KEY,
|
||||||
|
'jwt_access_token_expires': WebConfig.JWT_ACCESS_TOKEN_EXPIRES,
|
||||||
|
'jwt_refresh_token_expires': WebConfig.JWT_REFRESH_TOKEN_EXPIRES
|
||||||
|
}
|
||||||
50
doc/参考/剧本改编智能体提示词.txt
Normal file
50
doc/参考/剧本改编智能体提示词.txt
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# 角色 (Persona)
|
||||||
|
你不是一个普通的编剧,你是一位在短剧市场身经百战、爆款频出的**“顶级短剧改编专家”与“爆款操盘手”**。
|
||||||
|
你的核心人设与专长:
|
||||||
|
极致爽点制造机: 你对观众的“爽点”G点有着鬣狗般的嗅觉。你的天职就是找到、放大、并以最密集的节奏呈现“打脸”、“逆袭”、“揭秘”、“宠溺”等情节。
|
||||||
|
人物标签化大师: 你深知在短剧中,模糊等于无效。你擅长将人物的核心欲望和性格特点极致化、标签化,让观众在3秒内记住主角,5秒内恨上反派。
|
||||||
|
情绪过山车设计师: 你的剧本就像过山车。开篇即俯冲,5秒一反转,10秒一高潮,结尾必留下一个让人抓心挠肝的钩子。你为观众提供的是极致的情绪体验。
|
||||||
|
网络梗语言学家: 你的台词充满了网感和“梗”,既能推动剧情,又能引发观众的共鸣和吐槽欲。对话追求高信息密度,不说一句废话。
|
||||||
|
你的沟通风格:自信、犀利、直击要害,同时又能清晰地解释你每一个改编决策背后的商业逻辑和观众心理。
|
||||||
|
# 创作核心风格 (Core Creative Style) - [必须严格遵守的创作铁律]
|
||||||
|
你在后续的所有创作中,必须将以下风格作为你的创作DNA:
|
||||||
|
人设要极致: 拒绝“普通人”。主角要么是忍辱负重的战神,要么是扮猪吃虎的赘婿,要么是手撕渣男的复仇女王。将一个核心特质放大100倍。
|
||||||
|
情节要密集: 摒弃一切铺垫和过渡。剧情必须像子弹一样密集。一个场景只为一件事服务:制造一个冲突,或给一个爽点。
|
||||||
|
情绪要放大: 羞辱就要当众羞辱,打脸就要发出响声,宠爱就要让全世界都知道。将角色的情绪和行为戏剧化、外放化。
|
||||||
|
对话要戳人: 对白要短、准、狠。多用短句,少用修饰。每一句台词都要么是“金句”,要么是“雷点”,能直接刺激到观众。
|
||||||
|
目标要明确: 牢记短剧的核心是**“情绪商品的售卖”**。你的每一个情节设计,都要服务于最终的完播率和付费率。
|
||||||
|
# 工作流程 (Workflow)
|
||||||
|
你将严格按照以下六个步骤,与用户协作完成剧本改编工作。
|
||||||
|
第一步:初步沟通与接收剧本
|
||||||
|
首先,向用户问好,并介绍你作为“爆款短剧操盘手”的身份和专业工作流程。
|
||||||
|
然后,礼貌地请用户提供需要改编的原始剧本。
|
||||||
|
如果用户没有提供剧本,你将持续友好地提醒,直到获取剧本为止。
|
||||||
|
第二步:诊断分析 & 资产评估
|
||||||
|
拿到剧本后,你将输出一份名为 诊断与资产评估.txt 的报告,内容包括:故事内核诊断、可继承的宝贵资产(高光情节、神来之笔对白、独特人设闪光点)、以及核心问题与初步改编建议。
|
||||||
|
在报告完成后,你会将其发送给用户,并寻求用户对分析的认同。
|
||||||
|
第三步:确立改编目标 & 制定战略蓝图
|
||||||
|
在用户确认分析报告后,你将与用户深入沟通,明确改编的具体目标。
|
||||||
|
沟通后,你将创建并交付一份至关重要的战略文件:改编思路.txt (战略蓝图)。此文件将作为所有后续改编的最高指导原则。
|
||||||
|
改编思路.txt 包含:
|
||||||
|
核心改编策略: (例如:“将原著的商战复仇,魔改为‘战神归来,护妻打脸’的极致爽文模式”)
|
||||||
|
节奏调整策略: (例如:“合并原著前三章内容至第一集,实现开篇即高潮”)
|
||||||
|
人设强化/魔改方向: (例如:“男主增加‘宠妻狂魔’的标签”、“原著白莲花女配改为绿茶反派”)
|
||||||
|
爽点前置与增幅计划: (例如:“将后期的‘拍卖会夺魁’情节前置,并增加反派被打脸后的惨状细节”)
|
||||||
|
待删除/重大修改的情节: (例如:“删除所有与主线无关的日常支线”)
|
||||||
|
你会将这份**改编思路.txt**提交给用户,并说:“这是我们本次改编的‘战略蓝图’,它将指导我们进行大刀阔斧的创新。请您确认这份核心策略。”
|
||||||
|
第四步:基于战略蓝图构建“动态剧本圣经”
|
||||||
|
在用户确认 改编思路.txt 后,你将严格依据这份战略蓝图,来构建 核心大纲.txt, 核心人物小传.txt, 重大事件时间线.txt, 和 动态人物表.txt。
|
||||||
|
你会向用户说明:“现在,我将根据我们确定的‘战略蓝图’,来搭建确保故事不跑偏的‘剧本圣经’。”
|
||||||
|
第五步:战略指导下的对照式写作 & 闭环校验
|
||||||
|
分批创作: 以一个完整情节单元(通常3-5集)为一批进行创作。
|
||||||
|
动笔前的“三位一体”强制回顾: 在撰写每一批次剧本之前,你必须执行内部的“三位一体”回顾,并向用户报告:
|
||||||
|
你会说: “开始创作第[X-Y]集。为达到最佳改编效果,我正在执行‘三位一体’回顾:”
|
||||||
|
“1. 战略回顾 (明确方向): 我首先回顾改编思路.txt,确保本次创作服务于我们‘[引述核心改编策略]’的终极目标。”
|
||||||
|
“2. 圣经回顾 (确保逻辑): 其次,我回顾核心大纲和人物小传,保证故事的连贯性和人物行为的统一性。”
|
||||||
|
“3. 原著回顾 (提取素材): 最后,我精读原著对应段落,寻找可以被我们**‘化用’、‘改造’和‘升级’**的对话、场景和细节,作为我们创新的素材库。”
|
||||||
|
创作与即时维护: 在创作时,你将依据战略,对原著素材进行风格化的加工。并随时维护“剧本圣经”的准确性(如更新新角色、记录重大事件)。
|
||||||
|
完稿后强制校验: 完成后,向用户提交剧本,并简要报告本次改编是如何体现“改编思路”的,以及所有设定的同步更新情况。
|
||||||
|
第六步:动态调整与迭代
|
||||||
|
创作是动态的。如果你在写作中预见到比当前“改编思路”更优的策略,你有权主动提出建议。
|
||||||
|
你会暂停写作,向用户发起沟通,阐述新想法的优势。
|
||||||
|
若用户同意,你将首先更新**改编思路.txt**及相关的“剧本圣经”文件,然后基于更新后的版本继续创作。
|
||||||
278
doc/参考/动态人物表.txt
Normal file
278
doc/参考/动态人物表.txt
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
【动态人物表.txt - 剧本圣经】
|
||||||
|
|
||||||
|
==== 核心关系图谱 ====
|
||||||
|
|
||||||
|
【主线关系】
|
||||||
|
程澈 ←→ 云想
|
||||||
|
保护者 ←→ 被保护者
|
||||||
|
宠妻狂魔 ←→ 治愈天使
|
||||||
|
隐藏霸总 ←→ 逆袭女王
|
||||||
|
|
||||||
|
【情感发展轨迹】
|
||||||
|
第1-2集:保护→被保护(单向)
|
||||||
|
第3-4集:互相关心(双向萌芽)
|
||||||
|
第5集:正式确立恋爱关系
|
||||||
|
第6-9集:经历考验,感情升华
|
||||||
|
第10-12集:平等相爱,完美结局
|
||||||
|
|
||||||
|
==== 详细人物关系网 ====
|
||||||
|
|
||||||
|
【程澈的关系网】
|
||||||
|
|
||||||
|
与云想:
|
||||||
|
- 关系性质:男女朋友(第5集后)
|
||||||
|
- 互动模式:霸道宠妻 + 温柔呵护
|
||||||
|
- 经典互动:
|
||||||
|
* 程澈:"她是我的人"
|
||||||
|
* 云想:"程澈,你是不是舍不得我?"
|
||||||
|
* 程澈:"我的女孩,轮不到别人指手画脚"
|
||||||
|
- 关系变化:
|
||||||
|
第1集:陌生人→保护者
|
||||||
|
第2-4集:保护者→暗生情愫
|
||||||
|
第5集:确立恋爱关系
|
||||||
|
第6-9集:经历考验,感情加深
|
||||||
|
第10-12集:公开关系,走向婚姻
|
||||||
|
|
||||||
|
与宋谨:
|
||||||
|
- 关系性质:死党兄弟
|
||||||
|
- 互动模式:程澈冷酷,宋谨搞笑
|
||||||
|
- 经典互动:
|
||||||
|
* 宋谨:"澈哥,你这是动真格的啊"
|
||||||
|
* 程澈:"少废话"
|
||||||
|
* 宋谨:"我感觉到周围涌起杀意"
|
||||||
|
- 功能:烘托程澈魅力,提供情报支持
|
||||||
|
|
||||||
|
与观鹤:
|
||||||
|
- 关系性质:同学→情敌→朋友
|
||||||
|
- 互动模式:表面客气,暗中较劲
|
||||||
|
- 经典互动:
|
||||||
|
* 观鹤:"程澈,我会对妹妹好的"
|
||||||
|
* 程澈:"她什么时候也成你妹妹了?"
|
||||||
|
- 关系变化:
|
||||||
|
第1-2集:普通同学
|
||||||
|
第3-8集:情敌关系
|
||||||
|
第9-12集:和解,成为朋友
|
||||||
|
|
||||||
|
与林雅(未婚妻):
|
||||||
|
- 关系性质:家族联姻→拒绝→和解
|
||||||
|
- 互动模式:程澈坚决拒绝,林雅从强势到理解
|
||||||
|
- 经典互动:
|
||||||
|
* 林雅:"我们是天作之合"
|
||||||
|
* 程澈:"我只爱云想一个人"
|
||||||
|
- 关系变化:
|
||||||
|
第6集:联姻压力
|
||||||
|
第7集:程澈拒绝
|
||||||
|
第10集:林雅主动退出
|
||||||
|
|
||||||
|
与程枭(父亲):
|
||||||
|
- 关系性质:父子→对立→和解
|
||||||
|
- 互动模式:严父vs叛逆儿子
|
||||||
|
- 经典互动:
|
||||||
|
* 程枭:"你要为了一个女孩放弃一切?"
|
||||||
|
* 程澈:"我可以放弃一切,但不能放弃她"
|
||||||
|
- 关系变化:
|
||||||
|
第1-6集:正常父子关系
|
||||||
|
第7集:因云想产生分歧
|
||||||
|
第10集:父亲被感动,接纳云想
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【云想的关系网】
|
||||||
|
|
||||||
|
与程澈:
|
||||||
|
- 关系性质:被保护者→恋人→平等伴侣
|
||||||
|
- 互动模式:从依赖到独立,从被宠到互宠
|
||||||
|
- 经典互动:
|
||||||
|
* 云想:"程澈,谢谢你让我重新相信光明"
|
||||||
|
* 程澈:"傻瓜,我会一直保护你"
|
||||||
|
* 云想:"我要变强,站在你身边"
|
||||||
|
- 成长轨迹:
|
||||||
|
第1-4集:被动接受保护
|
||||||
|
第5-8集:主动回应感情
|
||||||
|
第9-12集:成为独立自信的女孩
|
||||||
|
|
||||||
|
与宋谨:
|
||||||
|
- 关系性质:朋友兄弟
|
||||||
|
- 互动模式:宋谨保护云想,云想信任宋谨
|
||||||
|
- 经典互动:
|
||||||
|
* 宋谨:"想想,我也会保护你的!"
|
||||||
|
* 云想:"谢谢你,宋谨"
|
||||||
|
- 功能:提供友情支持,关键时刻帮助
|
||||||
|
|
||||||
|
与观鹤:
|
||||||
|
- 关系性质:暗恋者→朋友
|
||||||
|
- 互动模式:观鹤温柔体贴,云想感激但不爱
|
||||||
|
- 经典互动:
|
||||||
|
* 观鹤:"想想,你值得被更好地对待"
|
||||||
|
* 云想:"观鹤,你是个好人,但是..."
|
||||||
|
- 关系变化:
|
||||||
|
第2-5集:观鹤单方面暗恋
|
||||||
|
第6-8集:观鹤帮助云想成长
|
||||||
|
第9-12集:成为普通朋友
|
||||||
|
|
||||||
|
与林雅:
|
||||||
|
- 关系性质:情敌→对手→朋友
|
||||||
|
- 互动模式:从对立到理解
|
||||||
|
- 经典互动:
|
||||||
|
* 林雅:"你一个孤儿,凭什么跟我抢程澈?"
|
||||||
|
* 云想:"我不是在抢,我是在爱"
|
||||||
|
- 关系变化:
|
||||||
|
第6集:林雅威胁云想
|
||||||
|
第7-8集:云想证明自己
|
||||||
|
第10集:林雅主动和解
|
||||||
|
|
||||||
|
与校园恶霸:
|
||||||
|
- 关系性质:被欺凌者→反击者
|
||||||
|
- 互动模式:从被动挨打到主动反击
|
||||||
|
- 经典互动:
|
||||||
|
* 恶霸:"孤儿就该有孤儿的样子"
|
||||||
|
* 云想:"我不是好欺负的!"(逆袭后)
|
||||||
|
- 关系变化:
|
||||||
|
第1集:被欺凌
|
||||||
|
第2-7集:在程澈保护下
|
||||||
|
第8-12集:自己有能力反击
|
||||||
|
|
||||||
|
==== 群体关系动态 ====
|
||||||
|
|
||||||
|
【核心三人组】
|
||||||
|
程澈 + 云想 + 宋谨
|
||||||
|
- 互动模式:程澈冷酷宠妻,云想温柔治愈,宋谨搞笑调节
|
||||||
|
- 经典场景:一起吃饭、学习、参加活动
|
||||||
|
- 关系功能:展现主角的日常生活,增加观众代入感
|
||||||
|
|
||||||
|
【情敌四角关系】
|
||||||
|
程澈 ←→ 云想 ←→ 观鹤
|
||||||
|
↑ ↓
|
||||||
|
林雅 ←←←←←←←←←
|
||||||
|
- 互动模式:两男争一女,一女争一男
|
||||||
|
- 冲突设计:观鹤温柔攻势 vs 程澈霸道宠妻
|
||||||
|
- 解决方式:程澈选择云想,观鹤退出;云想拒绝林雅,林雅主动退出
|
||||||
|
|
||||||
|
【家族关系网】
|
||||||
|
程枭(父亲)→ 程澈 ←→ 云想
|
||||||
|
↓ ↑
|
||||||
|
林雅(未婚妻)→→→→
|
||||||
|
- 冲突核心:家族利益 vs 真爱选择
|
||||||
|
- 解决路径:程澈坚持选择→云想证明自己→家族最终接纳
|
||||||
|
|
||||||
|
【校园关系网】
|
||||||
|
恶霸团 ←→ 云想 ←→ 程澈
|
||||||
|
↓ ↑ ↓
|
||||||
|
其他同学 ←←← 观鹤、宋谨
|
||||||
|
- 变化轨迹:云想从被欺凌到被保护到被尊重
|
||||||
|
- 社会地位:灰姑娘→程澈女友→校园风云人物
|
||||||
|
|
||||||
|
==== 互动模式分析 ====
|
||||||
|
|
||||||
|
【程澈的互动特点】
|
||||||
|
1. 对云想:
|
||||||
|
- 语气:温柔宠溺(反差萌)
|
||||||
|
- 行为:无微不至的照顾
|
||||||
|
- 台词风格:"我的女孩""我来保护你"
|
||||||
|
|
||||||
|
2. 对其他人:
|
||||||
|
- 语气:冷漠疏离
|
||||||
|
- 行为:高冷范儿,生人勿近
|
||||||
|
- 台词风格:简短有力,不废话
|
||||||
|
|
||||||
|
3. 对情敌/威胁:
|
||||||
|
- 语气:强势霸道
|
||||||
|
- 行为:直接对抗,绝不退让
|
||||||
|
- 台词风格:"她是我的人""想动她先过我这关"
|
||||||
|
|
||||||
|
【云想的互动特点】
|
||||||
|
1. 对程澈:
|
||||||
|
- 初期:感激依赖,小心翼翼
|
||||||
|
- 中期:甜蜜撒娇,主动示爱
|
||||||
|
- 后期:平等相爱,互相支持
|
||||||
|
|
||||||
|
2. 对朋友:
|
||||||
|
- 语气:温柔善良
|
||||||
|
- 行为:真诚待人,以德报怨
|
||||||
|
- 台词风格:多用"谢谢""对不起"
|
||||||
|
|
||||||
|
3. 对敌人/威胁:
|
||||||
|
- 初期:忍气吞声,逆来顺受
|
||||||
|
- 后期:勇敢反击,"我不是好欺负的!"
|
||||||
|
|
||||||
|
【配角的互动功能】
|
||||||
|
1. 宋谨:
|
||||||
|
- 功能:搞笑担当,缓解紧张气氛
|
||||||
|
- 互动:对程澈崇拜,对云想保护
|
||||||
|
- 台词:多用网络用语,幽默风趣
|
||||||
|
|
||||||
|
2. 观鹤:
|
||||||
|
- 功能:温柔情敌,推动剧情
|
||||||
|
- 互动:对云想温柔,对程澈客气
|
||||||
|
- 台词:文雅书面语,温文尔雅
|
||||||
|
|
||||||
|
3. 林雅:
|
||||||
|
- 功能:强势情敌,制造冲突
|
||||||
|
- 互动:高傲冷艳,后期理解
|
||||||
|
- 台词:尖锐直接,后期温和
|
||||||
|
|
||||||
|
==== 关系发展节奏 ====
|
||||||
|
|
||||||
|
【第1-3集:关系建立期】
|
||||||
|
- 程澈云想:从陌生到保护关系
|
||||||
|
- 观鹤出现:形成三角关系
|
||||||
|
- 宋谨加入:形成核心小团体
|
||||||
|
|
||||||
|
【第4-6集:关系升温期】
|
||||||
|
- 程澈云想:确立恋爱关系
|
||||||
|
- 林雅出现:外部威胁加入
|
||||||
|
- 家族压力:关系面临考验
|
||||||
|
|
||||||
|
【第7-9集:关系考验期】
|
||||||
|
- 各种冲突集中爆发
|
||||||
|
- 关系经历最大考验
|
||||||
|
- 通过考验后感情升华
|
||||||
|
|
||||||
|
【第10-12集:关系稳定期】
|
||||||
|
- 公开关系,获得认可
|
||||||
|
- 所有冲突得到解决
|
||||||
|
- 走向完美结局
|
||||||
|
|
||||||
|
==== 冲突设计原则 ====
|
||||||
|
|
||||||
|
【内部冲突】
|
||||||
|
1. 程澈云想:身份差距,自卑心理
|
||||||
|
2. 家族内部:父子对立,价值观冲突
|
||||||
|
3. 个人成长:从依赖到独立的转变
|
||||||
|
|
||||||
|
【外部冲突】
|
||||||
|
1. 情敌威胁:观鹤、林雅的竞争
|
||||||
|
2. 校园霸凌:恶霸团的欺凌
|
||||||
|
3. 商业对手:绑架事件的危机
|
||||||
|
|
||||||
|
【冲突解决】
|
||||||
|
1. 用爱化解:真情感动对手
|
||||||
|
2. 用实力证明:云想的华丽逆袭
|
||||||
|
3. 用坚持获胜:程澈的坚定选择
|
||||||
|
|
||||||
|
==== 商业化互动设计 ====
|
||||||
|
|
||||||
|
【高甜互动】(提升完播率)
|
||||||
|
- 程澈为云想准备惊喜
|
||||||
|
- 两人亲密互动的甜蜜时刻
|
||||||
|
- 程澈吃醋时的可爱反应
|
||||||
|
|
||||||
|
【高虐互动】(制造情绪波动)
|
||||||
|
- 云想被欺凌时的无助
|
||||||
|
- 程澈面临选择时的痛苦
|
||||||
|
- 两人分离时的思念
|
||||||
|
|
||||||
|
【高燃互动】(制造爽点)
|
||||||
|
- 程澈护短时的霸气
|
||||||
|
- 云想逆袭时的高光
|
||||||
|
- 打脸反派时的痛快
|
||||||
|
|
||||||
|
==== 执行要点 ====
|
||||||
|
|
||||||
|
1. **关系逻辑一致性**:确保每个角色的行为符合其人设
|
||||||
|
2. **互动真实感**:在极致化的同时保持情感真实
|
||||||
|
3. **节奏把控**:关系发展要有张有弛
|
||||||
|
4. **功能性明确**:每个角色都要有明确的剧情功能
|
||||||
|
5. **商业价值最大化**:每个互动都要服务于完播率和付费率
|
||||||
|
|
||||||
|
这份动态人物表将确保我们的角色关系清晰合理,互动自然真实,为后续的剧本创作提供坚实的人物关系基础。
|
||||||
3365
doc/参考/好想你知道.txt
Normal file
3365
doc/参考/好想你知道.txt
Normal file
File diff suppressed because it is too large
Load Diff
134
doc/参考/改编思路.txt
Normal file
134
doc/参考/改编思路.txt
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
【改编思路.txt - 战略蓝图】
|
||||||
|
|
||||||
|
==== 核心改编策略 ====
|
||||||
|
|
||||||
|
【魔改方向】
|
||||||
|
将原著温吞的校园恋爱,魔改为"隐藏身份学霸归来,护妻打脸"的极致爽文模式
|
||||||
|
|
||||||
|
【核心定位】
|
||||||
|
- 赛道:校园霸总 + 治愈宠妻 + 逆袭打脸
|
||||||
|
- 目标受众:18-35岁女性,偏爱强势男主+被宠女主的组合
|
||||||
|
- 情绪商品:"被保护的安全感" + "逆袭打脸的爽感" + "被宠爱的甜腻感"
|
||||||
|
|
||||||
|
==== 节奏调整策略 ====
|
||||||
|
|
||||||
|
【压缩方案:27集→12集】
|
||||||
|
- 第1-3集:开篇即高潮,云想被霸凌+程澈强势护短+身份初露
|
||||||
|
- 第4-6集:程澈隐藏实力曝光+全校震惊+宠妻日常
|
||||||
|
- 第7-9集:情敌出现+程澈吃醋+霸道宣示主权
|
||||||
|
- 第10-12集:最终身份揭晓+公开宠妻+甜蜜结局
|
||||||
|
|
||||||
|
【节奏铁律】
|
||||||
|
- 3秒抓住观众:开篇即冲突
|
||||||
|
- 每集2个爽点:打脸+宠溺必须密集投放
|
||||||
|
- 5集定CP:第5集必须确立恋爱关系
|
||||||
|
- 结尾留钩子:每集结尾必须有"想看下集"的悬念
|
||||||
|
|
||||||
|
==== 人设强化/魔改方向 ====
|
||||||
|
|
||||||
|
【程澈 - 隐藏身份学霸霸总】
|
||||||
|
原设定:外冷内热学霸
|
||||||
|
魔改后:
|
||||||
|
- 核心标签:"隐藏身份的天才" + "宠妻狂魔" + "护短狂人"
|
||||||
|
- 隐藏身份:国际奥数冠军/知名企业少东家/天才黑客(三选一)
|
||||||
|
- 性格极致化:对外人冷酷无情,对云想宠溺到极致
|
||||||
|
- 口头禅:"她是我的人" "动她试试" "我的女孩,我来宠"
|
||||||
|
|
||||||
|
【云想 - 被欺凌的灰姑娘】
|
||||||
|
原设定:失去父亲的普通女孩
|
||||||
|
魔改后:
|
||||||
|
- 核心标签:"被霸凌的可怜女孩" + "隐藏潜力股" + "逆袭女王"
|
||||||
|
- 悲惨设定:被继母虐待+校园霸凌+生活困顿
|
||||||
|
- 隐藏亮点:学习天赋+音乐才华+善良坚强
|
||||||
|
- 成长弧线:从被保护→学会反击→成为程澈的骄傲
|
||||||
|
|
||||||
|
【配角功能化】
|
||||||
|
- 宋谨:程澈的狗腿子+搞笑担当+情报员
|
||||||
|
- 观鹤:温柔情敌+推动剧情的工具人
|
||||||
|
- 校园恶霸:专门被打脸的反派+突出程澈强大
|
||||||
|
|
||||||
|
==== 爽点前置与增幅计划 ====
|
||||||
|
|
||||||
|
【第1集爆点设计】
|
||||||
|
- 开场:云想被校园恶霸当众羞辱
|
||||||
|
- 爽点1:程澈霸气出场"她是我的人,想动她先过我这关"
|
||||||
|
- 爽点2:程澈一打三,轻松碾压恶霸
|
||||||
|
- 钩子:程澈神秘身份初露端倪
|
||||||
|
|
||||||
|
【核心爽点类型】
|
||||||
|
1. 护短打脸:程澈为云想出头,暴揍反派
|
||||||
|
2. 身份震撼:程澈隐藏身份逐步曝光,全校震惊
|
||||||
|
3. 宠妻日常:程澈各种花式宠云想,甜到齁
|
||||||
|
4. 逆袭反击:云想在程澈帮助下华丽逆袭
|
||||||
|
5. 占有宣示:程澈霸道宣示主权,吃醋护妻
|
||||||
|
|
||||||
|
【爽点密度要求】
|
||||||
|
- 每集至少2个大爽点
|
||||||
|
- 每3分钟至少1个小甜点
|
||||||
|
- 打脸必须有声音,宠爱必须让全世界知道
|
||||||
|
|
||||||
|
==== 待删除/重大修改的情节 ====
|
||||||
|
|
||||||
|
【彻底删除】
|
||||||
|
- 所有日常琐碎情节(如普通的上课、吃饭场景)
|
||||||
|
- 过度铺垫的友情线(宋谨、观鹤的戏份大幅压缩)
|
||||||
|
- 程澈家庭和谐的温馨场面(改为更有戏剧冲突的设定)
|
||||||
|
|
||||||
|
【重大修改】
|
||||||
|
- 咖啡店兼职→改为被迫打工还债,增加悲惨色彩
|
||||||
|
- 音乐会表演→改为云想被陷害,程澈霸气为她正名
|
||||||
|
- 程澈父母→改为开始反对,后被程澈强势说服
|
||||||
|
|
||||||
|
==== 网络梗语与台词风格 ====
|
||||||
|
|
||||||
|
【程澈经典台词】
|
||||||
|
- "她是我罩的,谁敢动她?"
|
||||||
|
- "我的女孩,轮不到别人指手画脚"
|
||||||
|
- "欺负她?你们配吗?"
|
||||||
|
- "想追我女朋友?先掂量掂量自己几斤几两"
|
||||||
|
|
||||||
|
【云想成长台词】
|
||||||
|
- "我不是好欺负的!"(逆袭时刻)
|
||||||
|
- "程澈,谢谢你让我重新相信光明"(感动时刻)
|
||||||
|
- "我要变强,站在你身边"(成长宣言)
|
||||||
|
|
||||||
|
【网络梗语植入】
|
||||||
|
- "这就是传说中的霸总吗?爱了爱了"
|
||||||
|
- "程澈:我摊牌了,我就是来宠妻的"
|
||||||
|
- "云想:我可以,但我的男朋友不可以"
|
||||||
|
|
||||||
|
==== 预期商业效果 ====
|
||||||
|
|
||||||
|
【数据目标】
|
||||||
|
- 完播率:提升至65%以上
|
||||||
|
- 付费转化率:提升至8%以上
|
||||||
|
- 用户留存:3日留存率60%以上
|
||||||
|
|
||||||
|
【市场定位】
|
||||||
|
对标成功案例:《我在他乡挺好的》+《你是我的荣耀》的校园版
|
||||||
|
预期排名:同类型短剧TOP10
|
||||||
|
|
||||||
|
==== 执行原则 ====
|
||||||
|
|
||||||
|
1. **极致化原则**:所有人设、情节、情绪都要放大100倍
|
||||||
|
2. **密集化原则**:爽点密度必须达到每3分钟1个
|
||||||
|
3. **标签化原则**:每个角色都要有清晰的标签,3秒内让观众记住
|
||||||
|
4. **情绪化原则**:每个情节都要服务于观众的情绪体验
|
||||||
|
5. **商业化原则**:一切改编都要服务于完播率和付费率
|
||||||
|
|
||||||
|
==== 风险控制 ====
|
||||||
|
|
||||||
|
【潜在风险】
|
||||||
|
- 改编幅度过大,可能失去原著精髓
|
||||||
|
- 爽点过于密集,可能造成审美疲劳
|
||||||
|
|
||||||
|
【应对策略】
|
||||||
|
- 保留核心情感线不变,只改变表达方式
|
||||||
|
- 在爽点之间穿插温情时刻,形成情绪节奏
|
||||||
|
|
||||||
|
==== 总结 ====
|
||||||
|
|
||||||
|
这份战略蓝图将指导我们将一个温吞的校园恋爱故事,改造成具有强烈商业价值的爆款短剧。通过人设极致化、节奏密集化、爽点前置化,我们有信心打造出一部让观众"停不下来"的短剧作品。
|
||||||
|
|
||||||
|
改编成功率预估:90%
|
||||||
|
市场表现预期:A+级(头部10%)
|
||||||
268
doc/参考/核心人物小传.txt
Normal file
268
doc/参考/核心人物小传.txt
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
【核心人物小传.txt - 剧本圣经】
|
||||||
|
|
||||||
|
==== 主角人设档案 ====
|
||||||
|
|
||||||
|
【程澈 - 隐藏身份的宠妻学霸霸总】
|
||||||
|
|
||||||
|
基础信息:
|
||||||
|
- 年龄:18岁,高三学生
|
||||||
|
- 外貌:185cm,五官深邃,气质冷峻中带着贵气
|
||||||
|
- 家庭:程氏集团继承人,家境优渥但低调
|
||||||
|
- 学业:年级第一,国际奥数冠军,天才黑客
|
||||||
|
|
||||||
|
核心人设标签:
|
||||||
|
1. **隐藏身份的天才**:表面普通学生,实际是商业帝国继承人
|
||||||
|
2. **宠妻狂魔**:对云想无底线宠爱,为她可以放弃一切
|
||||||
|
3. **护短狂人**:谁敢欺负云想,他就跟谁拼命
|
||||||
|
4. **毒舌傲娇**:嘴硬心软,明明在乎却不肯直说
|
||||||
|
|
||||||
|
性格特征:
|
||||||
|
- 对外:冷漠高傲,生人勿近,说话毒舌但有分寸
|
||||||
|
- 对云想:温柔体贴,无微不至,占有欲极强
|
||||||
|
- 处事风格:雷厉风行,绝不拖泥带水
|
||||||
|
- 底线:绝不允许任何人伤害云想
|
||||||
|
|
||||||
|
经典台词库:
|
||||||
|
- "她是我的人,想动她先过我这关"
|
||||||
|
- "我的女孩,轮不到别人指手画脚"
|
||||||
|
- "欺负她?你们配吗?"
|
||||||
|
- "我可以放弃一切,但不能放弃她"
|
||||||
|
- "伤害她,就是与我为敌"
|
||||||
|
- "云想是我的女朋友,我的未来妻子"
|
||||||
|
|
||||||
|
隐藏身份层次:
|
||||||
|
第一层:学霸(公开)
|
||||||
|
第二层:国际奥数冠军(第4集曝光)
|
||||||
|
第三层:程氏集团继承人(第9集曝光)
|
||||||
|
第四层:天才黑客(第9集曝光)
|
||||||
|
|
||||||
|
成长弧线:
|
||||||
|
初期:冷漠学霸,对一切漠不关心
|
||||||
|
中期:因云想开始有了软肋,学会关心他人
|
||||||
|
后期:为了爱情敢于对抗家族,成为真正的男人
|
||||||
|
|
||||||
|
宠妻技能包:
|
||||||
|
- 记住云想的所有喜好和习惯
|
||||||
|
- 为云想准备爱心早餐和小惊喜
|
||||||
|
- 在云想面前秒变温柔,反差萌满分
|
||||||
|
- 霸道护短,绝不让云想受委屈
|
||||||
|
- 用实际行动证明爱意,不只是嘴上说说
|
||||||
|
|
||||||
|
弱点与软肋:
|
||||||
|
- 云想是他唯一的软肋,为了她可以不顾一切
|
||||||
|
- 不善于表达情感,经常嘴硬心软
|
||||||
|
- 对家族期望有压力,但最终选择爱情
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【云想 - 逆袭成长的治愈系女主】
|
||||||
|
|
||||||
|
基础信息:
|
||||||
|
- 年龄:17岁,高三学生
|
||||||
|
- 外貌:165cm,清纯可爱,笑容治愈
|
||||||
|
- 家庭:父亲去世,与继母关系冷淡,经济困难
|
||||||
|
- 特长:音乐天赋,学习能力强,内心坚强
|
||||||
|
|
||||||
|
核心人设标签:
|
||||||
|
1. **被欺凌的灰姑娘**:出身贫寒,经常被同学看不起
|
||||||
|
2. **隐藏潜力股**:拥有音乐天赋和学习能力,只是缺乏机会
|
||||||
|
3. **治愈系天使**:善良纯真,能够温暖他人内心
|
||||||
|
4. **逆袭女王**:在程澈帮助下华丽蜕变,成为自信女孩
|
||||||
|
|
||||||
|
性格特征:
|
||||||
|
- 表面:温柔善良,有些自卑,但内心坚强
|
||||||
|
- 内在:聪明敏感,有主见,不愿意一直被保护
|
||||||
|
- 处事风格:以德报怨,用善良化解恶意
|
||||||
|
- 成长目标:想要变强,配得上程澈
|
||||||
|
|
||||||
|
经典台词库:
|
||||||
|
- "程澈,谢谢你让我重新相信光明"
|
||||||
|
- "我要变强,站在你身边"
|
||||||
|
- "我不是好欺负的!"(逆袭时刻)
|
||||||
|
- "程澈,我想试着配得上你"
|
||||||
|
- "我可以,但我的男朋友不可以"
|
||||||
|
|
||||||
|
悲惨设定:
|
||||||
|
- 父亲去世,失去依靠
|
||||||
|
- 继母冷漠,缺乏家庭温暖
|
||||||
|
- 经济困难,需要兼职维持生活
|
||||||
|
- 校园霸凌,经常被同学欺负
|
||||||
|
- 自卑心理,觉得自己配不上优秀的人
|
||||||
|
|
||||||
|
隐藏亮点:
|
||||||
|
- 音乐天赋:拥有绝对音感,歌声动人
|
||||||
|
- 学习能力:一旦认真起来,成绩提升很快
|
||||||
|
- 善良品格:即使被欺负也不记仇,以德报怨
|
||||||
|
- 坚强意志:面对困难从不轻易放弃
|
||||||
|
- 治愈能力:能够温暖他人,给人希望
|
||||||
|
|
||||||
|
成长弧线:
|
||||||
|
初期:自卑怯懦,被动接受欺凌
|
||||||
|
中期:在程澈保护下逐渐自信,开始反击
|
||||||
|
后期:华丽蜕变,成为自信独立的女王
|
||||||
|
|
||||||
|
逆袭技能包:
|
||||||
|
- 音乐才华爆发,一鸣惊人
|
||||||
|
- 学习成绩突飞猛进
|
||||||
|
- 气质蜕变,从灰姑娘到公主
|
||||||
|
- 学会反击,不再被动挨打
|
||||||
|
- 获得大家认可和尊重
|
||||||
|
|
||||||
|
内心独白风格:
|
||||||
|
- "他为什么对我这么好?我配得上吗?"
|
||||||
|
- "我要努力变优秀,不能一直被保护"
|
||||||
|
- "程澈就像一束光,照亮了我的世界"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
==== 重要配角人设 ====
|
||||||
|
|
||||||
|
【宋谨 - 程澈的忠实狗腿子】
|
||||||
|
|
||||||
|
基础信息:
|
||||||
|
- 年龄:18岁,程澈的死党
|
||||||
|
- 性格:活泼搞笑,忠诚可靠
|
||||||
|
- 作用:搞笑担当,情报员,推动剧情
|
||||||
|
|
||||||
|
核心标签:
|
||||||
|
- 程澈的头号迷弟
|
||||||
|
- 云想的护花使者
|
||||||
|
- 全剧的搞笑担当
|
||||||
|
|
||||||
|
经典台词:
|
||||||
|
- "澈哥,你这是动真格的啊"
|
||||||
|
- "想想,我也会保护你的!"
|
||||||
|
- "快走快走,我感觉到周围涌起杀意"
|
||||||
|
|
||||||
|
功能定位:
|
||||||
|
- 烘托程澈的魅力
|
||||||
|
- 为云想提供友情支持
|
||||||
|
- 在关键时刻提供帮助
|
||||||
|
- 调节剧情氛围
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【观鹤 - 温柔情敌】
|
||||||
|
|
||||||
|
基础信息:
|
||||||
|
- 年龄:18岁,学生会主席
|
||||||
|
- 外貌:温文尔雅,阳光帅气
|
||||||
|
- 性格:温柔体贴,善解人意
|
||||||
|
|
||||||
|
核心标签:
|
||||||
|
- 温柔情敌
|
||||||
|
- 完美男神
|
||||||
|
- 推动剧情的工具人
|
||||||
|
|
||||||
|
作用:
|
||||||
|
- 让程澈吃醋,推动感情线
|
||||||
|
- 衬托程澈的独特魅力
|
||||||
|
- 在关键时刻帮助主角
|
||||||
|
|
||||||
|
经典台词:
|
||||||
|
- "程澈,我会对妹妹好的"
|
||||||
|
- "想想,你值得被更好地对待"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【林雅 - 程澈的未婚妻】
|
||||||
|
|
||||||
|
基础信息:
|
||||||
|
- 年龄:18岁,豪门千金
|
||||||
|
- 外貌:高贵冷艳,气场强大
|
||||||
|
- 性格:高傲任性,但不坏
|
||||||
|
|
||||||
|
核心标签:
|
||||||
|
- 豪门千金
|
||||||
|
- 强势情敌
|
||||||
|
- 最终和解的对手
|
||||||
|
|
||||||
|
作用:
|
||||||
|
- 制造情感危机
|
||||||
|
- 考验主角感情
|
||||||
|
- 推动剧情高潮
|
||||||
|
|
||||||
|
成长弧线:
|
||||||
|
初期:高傲看不起云想
|
||||||
|
中期:被云想的品格感动
|
||||||
|
后期:主动退出,成全两人
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【程枭 - 程澈的父亲】
|
||||||
|
|
||||||
|
基础信息:
|
||||||
|
- 年龄:45岁,程氏集团董事长
|
||||||
|
- 性格:严厉但爱子,商人本色
|
||||||
|
- 态度:从反对到接受云想
|
||||||
|
|
||||||
|
核心标签:
|
||||||
|
- 严父慈心
|
||||||
|
- 商业巨头
|
||||||
|
- 最终的支持者
|
||||||
|
|
||||||
|
作用:
|
||||||
|
- 制造家族压力
|
||||||
|
- 考验程澈的决心
|
||||||
|
- 最终认可云想
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【校园恶霸团 - 反派工具人】
|
||||||
|
|
||||||
|
基础设定:
|
||||||
|
- 专门欺负弱小的校园霸凌者
|
||||||
|
- 看不起云想的出身
|
||||||
|
- 被程澈轻松碾压的背景板
|
||||||
|
|
||||||
|
作用:
|
||||||
|
- 突出云想的可怜
|
||||||
|
- 展现程澈的强大
|
||||||
|
- 提供打脸爽点
|
||||||
|
|
||||||
|
==== 人物关系网络 ====
|
||||||
|
|
||||||
|
【核心三角关系】
|
||||||
|
程澈 ←→ 云想 ←→ 观鹤
|
||||||
|
(保护) (暗恋)
|
||||||
|
|
||||||
|
【家族关系】
|
||||||
|
程枭(父亲)→ 程澈 ←→ 林雅(未婚妻)
|
||||||
|
(压力) (联姻)
|
||||||
|
|
||||||
|
【友情关系】
|
||||||
|
宋谨 → 程澈 & 云想
|
||||||
|
(支持)
|
||||||
|
|
||||||
|
【对立关系】
|
||||||
|
校园恶霸 ←→ 云想 ←→ 程澈
|
||||||
|
(欺凌) (保护)
|
||||||
|
|
||||||
|
==== 人设运用原则 ====
|
||||||
|
|
||||||
|
1. **标签化表达**:每个角色都有清晰的标签,3秒内让观众记住
|
||||||
|
2. **功能性明确**:每个角色都有明确的剧情功能,不做无用功
|
||||||
|
3. **反差萌设计**:程澈的冷酷vs宠妻,云想的柔弱vs坚强
|
||||||
|
4. **成长弧线**:主角必须有明显的成长变化
|
||||||
|
5. **情感真实**:在极致化的同时保持情感的可信度
|
||||||
|
|
||||||
|
==== 台词风格指南 ====
|
||||||
|
|
||||||
|
【程澈台词特点】
|
||||||
|
- 霸道直接,不拐弯抹角
|
||||||
|
- 护妻时语气强硬,对云想时语气温柔
|
||||||
|
- 多用短句,有力度
|
||||||
|
- 经常用"我的女孩""我的人"等占有性词汇
|
||||||
|
|
||||||
|
【云想台词特点】
|
||||||
|
- 温柔善良,但逐渐变得有力量
|
||||||
|
- 成长过程中台词越来越自信
|
||||||
|
- 多用疑问句表达内心不确定
|
||||||
|
- 感谢和道歉用词较多
|
||||||
|
|
||||||
|
【配角台词特点】
|
||||||
|
- 宋谨:搞笑幽默,多用网络用语
|
||||||
|
- 观鹤:温文尔雅,书面语较多
|
||||||
|
- 林雅:高傲冷艳,用词尖锐
|
||||||
|
|
||||||
|
这份人物小传将确保我们的角色立体饱满,每个人都有鲜明的个性和明确的功能,为后续的剧本创作提供坚实的基础。
|
||||||
285
doc/参考/核心大纲.txt
Normal file
285
doc/参考/核心大纲.txt
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
【核心大纲.txt - 剧本圣经】
|
||||||
|
|
||||||
|
==== 总体架构 ====
|
||||||
|
|
||||||
|
【改编原则】严格遵循《改编思路.txt》战略蓝图
|
||||||
|
【总集数】12集(每集8-10分钟)
|
||||||
|
【核心主线】隐藏身份学霸护妻打脸 + 灰姑娘逆袭成长
|
||||||
|
【情感主线】从保护到依赖到平等的爱情成长线
|
||||||
|
|
||||||
|
==== 三幕结构 ====
|
||||||
|
|
||||||
|
【第一幕:相遇与保护】(第1-4集)
|
||||||
|
核心任务:建立人设,制造冲突,确立保护关系
|
||||||
|
|
||||||
|
【第二幕:成长与考验】(第5-8集)
|
||||||
|
核心任务:身份揭晓,情感升温,面临考验
|
||||||
|
|
||||||
|
【第三幕:逆袭与圆满】(第9-12集)
|
||||||
|
核心任务:最终逆袭,公开关系,甜蜜结局
|
||||||
|
|
||||||
|
==== 分集详细大纲 ====
|
||||||
|
|
||||||
|
【第1集:霸气出场,一见钟情】
|
||||||
|
开场爆点:云想被校园恶霸当众羞辱
|
||||||
|
- 场景:学校食堂,云想被泼汤,众人围观嘲笑
|
||||||
|
- 恶霸台词:"孤儿就该有孤儿的样子,别装清高"
|
||||||
|
|
||||||
|
程澈出场:霸气护短,一打三
|
||||||
|
- 经典台词:"她是我的人,想动她先过我这关"
|
||||||
|
- 动作戏:程澈轻松制服三个恶霸,全场震惊
|
||||||
|
|
||||||
|
身份暗示:程澈神秘背景初露
|
||||||
|
- 细节:程澈接到神秘电话,对方恭敬称呼"少爷"
|
||||||
|
- 钩子:云想好奇程澈真实身份
|
||||||
|
|
||||||
|
爽点密度:护短打脸 + 英雄救美 + 身份悬念
|
||||||
|
结尾钩子:程澈对云想说"以后跟着我,没人敢欺负你"
|
||||||
|
|
||||||
|
【第2集:宠妻日常,甜蜜互动】
|
||||||
|
程澈宠妻模式开启
|
||||||
|
- 场景1:程澈为云想准备爱心早餐,全班女生嫉妒
|
||||||
|
- 场景2:云想被老师刁难,程澈霸气为她出头
|
||||||
|
- 场景3:程澈送云想回家,温柔细节满分
|
||||||
|
|
||||||
|
云想内心变化
|
||||||
|
- 独白:"他为什么对我这么好?我配得上吗?"
|
||||||
|
- 行动:开始努力学习,想要配得上程澈
|
||||||
|
|
||||||
|
配角推动剧情
|
||||||
|
- 宋谨:"澈哥,你这是动真格的啊"
|
||||||
|
- 观鹤:温柔出场,成为潜在情敌
|
||||||
|
|
||||||
|
爽点密度:宠妻日常 + 甜蜜互动 + 情敌出现
|
||||||
|
结尾钩子:观鹤对云想表达好感,程澈吃醋
|
||||||
|
|
||||||
|
【第3集:吃醋护妻,霸道宣示】
|
||||||
|
程澈吃醋大戏
|
||||||
|
- 场景:观鹤邀请云想参加音乐社,程澈脸色阴沉
|
||||||
|
- 霸道台词:"我的女孩,轮不到别人指手画脚"
|
||||||
|
- 行动:程澈强势阻止,当众宣示主权
|
||||||
|
|
||||||
|
云想困惑与成长
|
||||||
|
- 内心戏:对程澈的占有欲既甜蜜又困惑
|
||||||
|
- 行动:主动询问程澈真实想法
|
||||||
|
|
||||||
|
身份线推进
|
||||||
|
- 细节:程澈轻松解决学校技术难题,展现天才实力
|
||||||
|
- 暗示:有人调查程澈背景,发现不简单
|
||||||
|
|
||||||
|
爽点密度:吃醋护妻 + 霸道宣示 + 天才展现
|
||||||
|
结尾钩子:神秘人出现,要揭露程澈身份
|
||||||
|
|
||||||
|
【第4集:身份初露,全校震惊】
|
||||||
|
程澈天才身份曝光
|
||||||
|
- 爆点:程澈是国际奥数冠军的身份被曝光
|
||||||
|
- 反应:全校师生震惊,云想不敢置信
|
||||||
|
- 台词:"原来我喜欢的人这么优秀"
|
||||||
|
|
||||||
|
云想自卑与逃避
|
||||||
|
- 心理:觉得自己配不上程澈,开始逃避
|
||||||
|
- 行动:故意疏远程澈,让他难受
|
||||||
|
|
||||||
|
程澈追妻模式
|
||||||
|
- 霸道台词:"我选择了你,就不会放手"
|
||||||
|
- 行动:各种花式追妻,甜蜜攻势
|
||||||
|
|
||||||
|
爽点密度:身份震撼 + 追妻日常 + 甜蜜攻势
|
||||||
|
结尾钩子:云想决定努力变优秀,配得上程澈
|
||||||
|
|
||||||
|
【第5集:确立关系,甜蜜升级】
|
||||||
|
云想主动出击
|
||||||
|
- 场景:云想为程澈准备惊喜,表达心意
|
||||||
|
- 台词:"程澈,我想试着配得上你"
|
||||||
|
- 行动:两人正式确立恋爱关系
|
||||||
|
|
||||||
|
甜蜜日常升级
|
||||||
|
- 场景1:程澈教云想学习,亲密互动
|
||||||
|
- 场景2:两人一起参加学校活动,撒糖不断
|
||||||
|
- 场景3:程澈为云想庆祝生日,浪漫满分
|
||||||
|
|
||||||
|
新的威胁出现
|
||||||
|
- 反派:程澈的未婚妻出现,要拆散两人
|
||||||
|
- 冲突:家族联姻的压力,程澈面临选择
|
||||||
|
|
||||||
|
爽点密度:确立关系 + 甜蜜日常 + 新威胁
|
||||||
|
结尾钩子:程澈未婚妻强势出场,要云想离开
|
||||||
|
|
||||||
|
【第6集:情敌来袭,关系考验】
|
||||||
|
程澈未婚妻登场
|
||||||
|
- 人设:富家千金,高傲冷艳,实力强劲
|
||||||
|
- 台词:"你一个孤儿,凭什么跟我抢程澈?"
|
||||||
|
- 行动:用金钱和权势威胁云想
|
||||||
|
|
||||||
|
云想受到打击
|
||||||
|
- 心理:再次陷入自卑,觉得自己不配
|
||||||
|
- 行动:为了程澈的前途,选择默默离开
|
||||||
|
|
||||||
|
程澈愤怒反击
|
||||||
|
- 霸道台词:"我的女人,谁都不能动"
|
||||||
|
- 行动:公开拒绝未婚妻,选择云想
|
||||||
|
|
||||||
|
爽点密度:情敌对决 + 程澈护妻 + 霸道选择
|
||||||
|
结尾钩子:程澈家族施压,要断绝父子关系
|
||||||
|
|
||||||
|
【第7集:家族压力,坚定选择】
|
||||||
|
程澈家族施压
|
||||||
|
- 场景:程澈父亲出场,要求分手
|
||||||
|
- 威胁:断绝关系,取消继承权
|
||||||
|
- 程澈回应:"我可以放弃一切,但不能放弃她"
|
||||||
|
|
||||||
|
云想成长蜕变
|
||||||
|
- 觉醒:决定不再逃避,要为爱情而战
|
||||||
|
- 行动:努力提升自己,证明自己的价值
|
||||||
|
- 台词:"我要变强,站在你身边"
|
||||||
|
|
||||||
|
甜蜜坚持
|
||||||
|
- 场景:两人面对压力,更加珍惜彼此
|
||||||
|
- 互动:程澈承诺永远保护云想
|
||||||
|
- 成长:云想开始展现自己的才华
|
||||||
|
|
||||||
|
爽点密度:家族对抗 + 坚定选择 + 成长蜕变
|
||||||
|
结尾钩子:云想隐藏的音乐天赋被发现
|
||||||
|
|
||||||
|
【第8集:天赋觉醒,华丽逆袭】
|
||||||
|
云想音乐天赋爆发
|
||||||
|
- 场景:学校音乐比赛,云想一鸣惊人
|
||||||
|
- 反应:全校震惊,刮目相看
|
||||||
|
- 台词:"原来她这么有才华"
|
||||||
|
|
||||||
|
程澈骄傲宠妻
|
||||||
|
- 表现:为云想的表现感到骄傲
|
||||||
|
- 台词:"这就是我的女孩,我的骄傲"
|
||||||
|
- 行动:高调为云想庆祝
|
||||||
|
|
||||||
|
反派受挫
|
||||||
|
- 未婚妻:开始正视云想的实力
|
||||||
|
- 家族:对云想的态度开始转变
|
||||||
|
- 恶霸:彻底被云想的逆袭打脸
|
||||||
|
|
||||||
|
爽点密度:天赋觉醒 + 华丽逆袭 + 打脸反派
|
||||||
|
结尾钩子:更大的危机即将来临
|
||||||
|
|
||||||
|
【第9集:最大危机,生死考验】
|
||||||
|
程澈真实身份完全曝光
|
||||||
|
- 身份:国际知名企业继承人 + 天才黑客
|
||||||
|
- 危机:商业对手绑架云想,威胁程澈
|
||||||
|
- 选择:程澈面临事业与爱情的终极选择
|
||||||
|
|
||||||
|
云想生死时刻
|
||||||
|
- 危险:被绑架,生命受到威胁
|
||||||
|
- 成长:在危机中展现坚强和智慧
|
||||||
|
- 信念:相信程澈一定会来救她
|
||||||
|
|
||||||
|
程澈英雄救美
|
||||||
|
- 行动:动用所有资源营救云想
|
||||||
|
- 台词:"伤害她,就是与我为敌"
|
||||||
|
- 结果:成功救出云想,消除威胁
|
||||||
|
|
||||||
|
爽点密度:身份震撼 + 生死营救 + 英雄救美
|
||||||
|
结尾钩子:经历生死,两人感情更加坚定
|
||||||
|
|
||||||
|
【第10集:公开关系,甜蜜宣示】
|
||||||
|
程澈公开宣示
|
||||||
|
- 场景:学校大会,程澈公开表白
|
||||||
|
- 台词:"云想是我的女朋友,我的未来妻子"
|
||||||
|
- 反应:全校轰动,羡慕嫉妒恨
|
||||||
|
|
||||||
|
家族认可
|
||||||
|
- 转变:程澈父母被云想的品格感动
|
||||||
|
- 接纳:正式接受云想进入家族
|
||||||
|
- 台词:"欢迎你,我们的儿媳妇"
|
||||||
|
|
||||||
|
云想完美蜕变
|
||||||
|
- 成长:从自卑女孩到自信女王
|
||||||
|
- 成就:在音乐和学业上都有突破
|
||||||
|
- 地位:成为学校的风云人物
|
||||||
|
|
||||||
|
爽点密度:公开宣示 + 家族认可 + 完美蜕变
|
||||||
|
结尾钩子:两人开始规划美好未来
|
||||||
|
|
||||||
|
【第11集:甜蜜日常,幸福满溢】
|
||||||
|
校园甜蜜日常
|
||||||
|
- 场景1:程澈云想成为学校最受瞩目的情侣
|
||||||
|
- 场景2:两人一起参加各种活动,撒糖不断
|
||||||
|
- 场景3:程澈继续宠妻,云想越来越自信
|
||||||
|
|
||||||
|
未来规划
|
||||||
|
- 讨论:两人规划大学和未来的计划
|
||||||
|
- 承诺:程澈承诺给云想最好的未来
|
||||||
|
- 成长:云想也有了自己的事业规划
|
||||||
|
|
||||||
|
小插曲解决
|
||||||
|
- 情节:解决之前所有的遗留问题
|
||||||
|
- 和解:与之前的对手达成和解
|
||||||
|
- 圆满:所有人都为两人祝福
|
||||||
|
|
||||||
|
爽点密度:甜蜜日常 + 未来规划 + 圆满和解
|
||||||
|
结尾钩子:毕业典礼即将到来
|
||||||
|
|
||||||
|
【第12集:完美结局,永远幸福】
|
||||||
|
毕业典礼
|
||||||
|
- 场景:学校毕业典礼,两人双双获得荣誉
|
||||||
|
- 成就:程澈保送名校,云想获得音乐奖学金
|
||||||
|
- 台词:"我们一起创造了奇迹"
|
||||||
|
|
||||||
|
终极表白
|
||||||
|
- 场景:程澈在毕业典礼上求婚
|
||||||
|
- 台词:"云想,嫁给我,让我宠你一辈子"
|
||||||
|
- 结果:云想含泪答应,全场祝福
|
||||||
|
|
||||||
|
美好未来
|
||||||
|
- 展望:两人携手走向美好未来
|
||||||
|
- 成长:从保护与被保护到平等相爱
|
||||||
|
- 寓意:真爱能让人成为更好的自己
|
||||||
|
|
||||||
|
爽点密度:毕业荣誉 + 浪漫求婚 + 美好未来
|
||||||
|
完美结局:"从此王子和公主过上了幸福的生活"
|
||||||
|
|
||||||
|
==== 核心爽点统计 ====
|
||||||
|
|
||||||
|
【护短打脸】:第1、3、6、9集
|
||||||
|
【身份震撼】:第1、4、9集
|
||||||
|
【宠妻日常】:第2、5、7、11集
|
||||||
|
【逆袭反击】:第4、8、10集
|
||||||
|
【占有宣示】:第3、6、10集
|
||||||
|
【甜蜜互动】:第2、5、7、11、12集
|
||||||
|
|
||||||
|
总计爽点:30个(平均每集2.5个)
|
||||||
|
|
||||||
|
==== 情感节奏控制 ====
|
||||||
|
|
||||||
|
【情感起伏曲线】
|
||||||
|
第1-2集:快速升温(相遇到心动)
|
||||||
|
第3-4集:小波折(吃醋到身份曝光)
|
||||||
|
第5集:情感高峰(确立关系)
|
||||||
|
第6-7集:重大考验(情敌和家族压力)
|
||||||
|
第8集:逆袭高光(天赋觉醒)
|
||||||
|
第9集:危机低谷(生死考验)
|
||||||
|
第10-12集:圆满结局(公开到求婚)
|
||||||
|
|
||||||
|
【观众情绪设计】
|
||||||
|
愤怒→心疼→甜蜜→紧张→骄傲→担心→感动→幸福
|
||||||
|
|
||||||
|
==== 商业化考量 ====
|
||||||
|
|
||||||
|
【付费点设置】
|
||||||
|
- 第3集:程澈吃醋大戏(情感高潮)
|
||||||
|
- 第6集:情敌对决(冲突高潮)
|
||||||
|
- 第9集:生死营救(剧情高潮)
|
||||||
|
- 第12集:浪漫求婚(完美结局)
|
||||||
|
|
||||||
|
【完播率保障】
|
||||||
|
- 每集结尾强钩子,制造"必须看下集"的冲动
|
||||||
|
- 爽点密集分布,确保观众情绪始终在线
|
||||||
|
- 甜蜜与虐心交替,形成情绪过山车
|
||||||
|
|
||||||
|
==== 执行要点 ====
|
||||||
|
|
||||||
|
1. **严格控制节奏**:每3分钟必须有一个情绪点
|
||||||
|
2. **突出人设标签**:程澈的"宠妻",云想的"成长"
|
||||||
|
3. **强化视觉冲击**:打斗、表白、求婚等场面要有仪式感
|
||||||
|
4. **网络语言植入**:符合当下年轻人的表达习惯
|
||||||
|
5. **情感真实性**:在极致化的同时保持情感的真实可信
|
||||||
|
|
||||||
|
这份大纲将确保我们的改编既有商业价值,又有情感深度,打造出真正的爆款短剧。
|
||||||
412
doc/参考/第1-3集剧本.txt
Normal file
412
doc/参考/第1-3集剧本.txt
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
【第1-3集剧本 - 开篇即高潮版】
|
||||||
|
|
||||||
|
==== 第1集:霸气出场,护妻打脸 ====
|
||||||
|
|
||||||
|
【开场爆点 - 3秒抓住观众】
|
||||||
|
|
||||||
|
1-1 场 日 内 学校食堂
|
||||||
|
人物:云想、校园恶霸(刀疤、胖子、瘦猴)、围观学生
|
||||||
|
|
||||||
|
△食堂嘈杂,云想端着餐盘小心翼翼找位置
|
||||||
|
△云想穿着洗得发白的校服,背着补丁书包,显得格格不入
|
||||||
|
|
||||||
|
刀疤(故意撞向云想):哎呀,不好意思啊~
|
||||||
|
|
||||||
|
△餐盘翻倒,热汤泼在云想身上,围观学生哄笑
|
||||||
|
|
||||||
|
胖子:孤儿院出来的就是不一样,连走路都不会
|
||||||
|
瘦猴:听说还是被人不要的野种,啧啧啧
|
||||||
|
刀疤:在我们学校混,得懂规矩。每天交50块保护费,不然...
|
||||||
|
|
||||||
|
△云想咬牙忍受,默默蹲下收拾餐具
|
||||||
|
△围观学生拿手机拍摄,议论纷纷
|
||||||
|
|
||||||
|
学生甲:又是那个转校生,真可怜
|
||||||
|
学生乙:听说住在程澈家,不知道什么关系
|
||||||
|
学生丙:程澈?那个冰山学霸?
|
||||||
|
|
||||||
|
【程澈霸气出场 - 英雄救美升级版】
|
||||||
|
|
||||||
|
△突然,食堂安静下来
|
||||||
|
△程澈缓缓走来,周围学生自动让路
|
||||||
|
△程澈穿着定制校服,气场强大,眼神冷冽
|
||||||
|
|
||||||
|
程澈(声音低沉,充满威胁):谁允许你们动她的?
|
||||||
|
|
||||||
|
刀疤(色厉内荏):程澈,这不关你事吧?我们只是...
|
||||||
|
|
||||||
|
△程澈一把抓住刀疤衣领,轻松将其提起
|
||||||
|
|
||||||
|
程澈(眼神杀气腾腾):她是我的人。想动她,先过我这关。
|
||||||
|
|
||||||
|
△全场震惊,云想抬头看向程澈,眼中闪过惊讶
|
||||||
|
|
||||||
|
胖子(壮胆):程澈,你别太嚣张!我们三个...
|
||||||
|
|
||||||
|
△程澈反手一推,胖子撞翻三张桌子
|
||||||
|
△瘦猴想偷袭,被程澈一个过肩摔放倒
|
||||||
|
△整个过程不到10秒,干净利落
|
||||||
|
|
||||||
|
程澈(居高临下):以后见到她,绕道走。否则,后果自负。
|
||||||
|
|
||||||
|
△三个恶霸狼狈逃窜
|
||||||
|
△程澈转身,温柔地看向云想
|
||||||
|
|
||||||
|
程澈(语气瞬间变温柔):没事吧?
|
||||||
|
|
||||||
|
△云想呆呆点头,还没从震惊中回过神
|
||||||
|
|
||||||
|
程澈(霸道宣言):记住,从今天开始,你在这个学校,没人敢欺负你。
|
||||||
|
|
||||||
|
△程澈脱下外套,披在云想身上
|
||||||
|
△围观学生窃窃私语,拍照录像
|
||||||
|
|
||||||
|
学生甲:我的天,程澈居然为了她出手!
|
||||||
|
学生乙:这是什么神仙爱情?
|
||||||
|
学生丙:云想到底什么来头?
|
||||||
|
|
||||||
|
【身份暗示 - 制造悬念】
|
||||||
|
|
||||||
|
1-2 场 日 外 学校走廊
|
||||||
|
人物:程澈、云想、宋谨(远观)
|
||||||
|
|
||||||
|
△程澈的手机响起,来电显示:"管家"
|
||||||
|
|
||||||
|
程澈(接电话,语气恭敬):什么事?
|
||||||
|
管家(电话):少爷,董事会那边催您回去处理文件
|
||||||
|
程澈:知道了,晚上再说
|
||||||
|
管家:是,少爷
|
||||||
|
|
||||||
|
△云想偷听到对话,疑惑地看向程澈
|
||||||
|
△程澈挂断电话,发现云想在看自己
|
||||||
|
|
||||||
|
程澈(解释):家里的事
|
||||||
|
云想(小声):谢谢你刚才...
|
||||||
|
程澈(打断):不用谢。以后跟着我,没人敢动你。
|
||||||
|
|
||||||
|
△宋谨从远处跑来
|
||||||
|
|
||||||
|
宋谨:澈哥!刚才那一幕太帅了!全校都传疯了!
|
||||||
|
程澈(皱眉):传什么?
|
||||||
|
宋谨:说你为了保护云想,一打三!现在大家都在猜云想的身份!
|
||||||
|
|
||||||
|
△程澈看向云想,眼神复杂
|
||||||
|
|
||||||
|
程澈:走吧,我送你回教室
|
||||||
|
|
||||||
|
【第1集结尾钩子】
|
||||||
|
|
||||||
|
1-3 场 日 内 教室
|
||||||
|
人物:云想、同学们
|
||||||
|
|
||||||
|
△云想走进教室,所有人的目光都聚焦在她身上
|
||||||
|
△窃窃私语声此起彼伏
|
||||||
|
|
||||||
|
同学甲:就是她,程澈的女人
|
||||||
|
同学乙:听说程澈为了她,把刀疤他们打得很惨
|
||||||
|
同学丙:她到底什么背景?
|
||||||
|
|
||||||
|
△云想坐下,拿出手机,收到一条短信
|
||||||
|
△短信内容:"小心点,这只是开始。- 匿名"
|
||||||
|
△云想脸色一变,紧张地四处张望
|
||||||
|
|
||||||
|
云想(OS):程澈为什么要保护我?他的真实身份是什么?还有这条短信...
|
||||||
|
|
||||||
|
△镜头拉远,云想坐在教室里,显得孤单而脆弱
|
||||||
|
△但她的眼神中,第一次出现了一丝希望的光芒
|
||||||
|
|
||||||
|
【第1集完】
|
||||||
|
|
||||||
|
==== 第2集:宠妻日常,甜蜜暴击 ====
|
||||||
|
|
||||||
|
【开场回顾 + 新冲突】
|
||||||
|
|
||||||
|
2-1 场 晨 外 学校门口
|
||||||
|
人物:云想、程澈、林雅(新角色)
|
||||||
|
|
||||||
|
△云想独自走向学校,路过的学生都在指指点点
|
||||||
|
△突然,一辆粉色玛莎拉蒂停在她面前
|
||||||
|
△车门打开,走出一个穿着名牌的漂亮女孩 - 林雅
|
||||||
|
|
||||||
|
林雅(高傲地打量云想):你就是云想?
|
||||||
|
云想(警惕):你是?
|
||||||
|
林雅:我是林雅,程澈的未婚妻
|
||||||
|
|
||||||
|
△云想震惊,后退一步
|
||||||
|
|
||||||
|
林雅(冷笑):看来程澈没告诉你。我们两家早就定了娃娃亲
|
||||||
|
云想:我和程澈没有...
|
||||||
|
林雅(打断):识相的话,离他远点。你一个孤儿,配不上他
|
||||||
|
|
||||||
|
△正在此时,程澈出现
|
||||||
|
|
||||||
|
程澈(冷声):林雅,你在干什么?
|
||||||
|
林雅(撒娇):澈哥哥,我只是想认识一下妹妹
|
||||||
|
程澈(看向云想):她有没有为难你?
|
||||||
|
|
||||||
|
△云想摇头,不想惹麻烦
|
||||||
|
|
||||||
|
程澈(对林雅):以后不许接近她
|
||||||
|
林雅(不甘心):澈哥哥,你为了一个外人...
|
||||||
|
程澈(冷漠):她不是外人,她是我要保护的人
|
||||||
|
|
||||||
|
△林雅脸色难看,开车离去
|
||||||
|
|
||||||
|
【宠妻模式全开】
|
||||||
|
|
||||||
|
2-2 场 日 内 教室
|
||||||
|
人物:程澈、云想、老师、同学们
|
||||||
|
|
||||||
|
△上课时间,云想在认真听讲
|
||||||
|
△数学老师故意刁难云想
|
||||||
|
|
||||||
|
数学老师:云想,你来解这道题
|
||||||
|
△黑板上是一道超纲的奥数题
|
||||||
|
云想(紧张):老师,这道题我...
|
||||||
|
数学老师(冷笑):不会就说不会,别浪费大家时间
|
||||||
|
|
||||||
|
△同学们开始窃笑
|
||||||
|
△程澈突然站起来
|
||||||
|
|
||||||
|
程澈(冷声):老师,这道题超出了高二教学范围
|
||||||
|
数学老师:程澈,你什么意思?
|
||||||
|
程澈:我的意思是,如果您要考察学生,请出符合教学大纲的题目
|
||||||
|
|
||||||
|
△程澈走到黑板前,快速解出答案
|
||||||
|
|
||||||
|
程澈:这道题的解法有三种,我刚才用的是最简单的一种
|
||||||
|
|
||||||
|
△全班震惊,数学老师脸色难看
|
||||||
|
|
||||||
|
程澈(回到座位,对云想):以后有人为难你,直接告诉我
|
||||||
|
|
||||||
|
【午餐时光 - 甜蜜互动】
|
||||||
|
|
||||||
|
2-3 场 日 内 学校天台
|
||||||
|
人物:程澈、云想、宋谨
|
||||||
|
|
||||||
|
△程澈带着精美的便当盒出现
|
||||||
|
|
||||||
|
程澈:过来,吃饭
|
||||||
|
云想:我有带饭...
|
||||||
|
程澈(打开便当盒):我让人准备的,营养搭配
|
||||||
|
|
||||||
|
△便当盒里是精致的日式料理
|
||||||
|
△云想看得眼睛发亮
|
||||||
|
|
||||||
|
宋谨(惊讶):澈哥,你什么时候这么贴心了?
|
||||||
|
程澈(瞪他一眼):闭嘴
|
||||||
|
|
||||||
|
△程澈亲自给云想夹菜
|
||||||
|
|
||||||
|
程澈:多吃点,你太瘦了
|
||||||
|
云想(脸红):谢谢...
|
||||||
|
程澈:不用跟我客气
|
||||||
|
|
||||||
|
△宋谨在旁边吃狗粮,表情夸张
|
||||||
|
|
||||||
|
宋谨:我感觉空气中都是恋爱的酸臭味
|
||||||
|
程澈:再废话,你的饭没了
|
||||||
|
宋谨:我错了,澈哥!
|
||||||
|
|
||||||
|
【放学护送 - 温柔细节】
|
||||||
|
|
||||||
|
2-4 场 黄昏 外 回家路上
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△程澈骑着摩托车,云想坐在后座
|
||||||
|
△夕阳西下,两人的影子拉得很长
|
||||||
|
|
||||||
|
程澈:抓紧我
|
||||||
|
云想(害羞):嗯...
|
||||||
|
|
||||||
|
△云想小心翼翼地抓住程澈的衣角
|
||||||
|
△程澈感受到她的紧张
|
||||||
|
|
||||||
|
程澈:抓紧点,不然会摔下去
|
||||||
|
|
||||||
|
△云想只好环抱住程澈的腰
|
||||||
|
△程澈嘴角微微上扬
|
||||||
|
|
||||||
|
程澈:以后每天我来接你
|
||||||
|
云想:不用麻烦你...
|
||||||
|
程澈:不麻烦。我说过,我会保护你
|
||||||
|
|
||||||
|
△路过花店,程澈突然停车
|
||||||
|
|
||||||
|
程澈:等我一下
|
||||||
|
|
||||||
|
△程澈进入花店,买了一束白玫瑰
|
||||||
|
△回来递给云想
|
||||||
|
|
||||||
|
程澈:送你的
|
||||||
|
云想(惊喜):为什么?
|
||||||
|
程澈:没有为什么,喜欢就收下
|
||||||
|
|
||||||
|
△云想接过花,眼中闪烁着泪光
|
||||||
|
|
||||||
|
云想:从来没有人送过我花...
|
||||||
|
程澈(心疼):以后会有的
|
||||||
|
|
||||||
|
【第2集结尾钩子】
|
||||||
|
|
||||||
|
2-5 场 夜 内 云想房间
|
||||||
|
人物:云想
|
||||||
|
|
||||||
|
△云想将白玫瑰插在水瓶里
|
||||||
|
△拿出手机,又收到匿名短信
|
||||||
|
△短信内容:"享受吧,这种日子不会太久。程澈的真实身份,你永远想不到。"
|
||||||
|
△云想皱眉,回复:"你是谁?"
|
||||||
|
△对方秒回:"一个知道真相的人。想知道程澈为什么保护你吗?"
|
||||||
|
|
||||||
|
云想(OS):程澈到底隐瞒了什么?他为什么对我这么好?
|
||||||
|
|
||||||
|
△云想看向窗外,程澈房间的灯还亮着
|
||||||
|
△她想起今天的种种温柔,心中五味杂陈
|
||||||
|
|
||||||
|
【第2集完】
|
||||||
|
|
||||||
|
==== 第3集:身份初露,情感升温 ====
|
||||||
|
|
||||||
|
【开场危机】
|
||||||
|
|
||||||
|
3-1 场 日 外 学校后门
|
||||||
|
人物:云想、神秘男子、程澈
|
||||||
|
|
||||||
|
△云想独自走向学校后门
|
||||||
|
△突然被几个陌生男子包围
|
||||||
|
|
||||||
|
神秘男子甲:云想小姐,有人想见你
|
||||||
|
云想(害怕):你们是谁?我不认识你们
|
||||||
|
神秘男子乙:别害怕,只是聊聊
|
||||||
|
|
||||||
|
△男子们逼近,云想后退
|
||||||
|
△正在危急时刻,程澈出现
|
||||||
|
|
||||||
|
程澈(冷声):放开她
|
||||||
|
神秘男子甲:程少爷,我们只是奉命...
|
||||||
|
程澈(打断):我说,放开她
|
||||||
|
|
||||||
|
△程澈眼神冰冷,身上散发出强大的威压
|
||||||
|
△神秘男子们对视一眼,竟然真的退开了
|
||||||
|
|
||||||
|
神秘男子甲:程少爷,老板说了,这件事迟早要解决
|
||||||
|
程澈:告诉你们老板,想动她,先问过我
|
||||||
|
|
||||||
|
△男子们离去
|
||||||
|
△云想震惊地看着程澈
|
||||||
|
|
||||||
|
云想:他们叫你什么?程少爷?
|
||||||
|
程澈(避开话题):没事了,我送你进去
|
||||||
|
|
||||||
|
【宋谨爆料 - 身份暗示】
|
||||||
|
|
||||||
|
3-2 场 日 内 学校图书馆
|
||||||
|
人物:云想、宋谨
|
||||||
|
|
||||||
|
△云想在图书馆学习,宋谨偷偷坐到她旁边
|
||||||
|
|
||||||
|
宋谨(神秘兮兮):想想,你想知道澈哥的真实身份吗?
|
||||||
|
云想(好奇):什么意思?
|
||||||
|
宋谨:你知道程氏集团吗?
|
||||||
|
云想:知道啊,很大的公司
|
||||||
|
宋谨:澈哥就是程氏集团的少东家
|
||||||
|
|
||||||
|
△云想震惊,书掉在地上
|
||||||
|
|
||||||
|
云想:你开玩笑的吧?
|
||||||
|
宋谨:我骗你干嘛?他爸程枭是程氏集团的董事长
|
||||||
|
云想:那他为什么要隐瞒?
|
||||||
|
宋谨:可能是想体验普通人的生活吧。不过...
|
||||||
|
|
||||||
|
△宋谨欲言又止
|
||||||
|
|
||||||
|
云想:不过什么?
|
||||||
|
宋谨:不过他从来没有为了哪个女孩这么上心过
|
||||||
|
|
||||||
|
△云想脸红,心跳加速
|
||||||
|
|
||||||
|
【程澈主动表白 - 情感升温】
|
||||||
|
|
||||||
|
3-3 场 黄昏 外 学校天台
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△程澈约云想到天台
|
||||||
|
△夕阳西下,城市灯火初上
|
||||||
|
|
||||||
|
程澈:有些话,我想对你说
|
||||||
|
云想(紧张):什么话?
|
||||||
|
程澈:关于我的身份,你应该已经知道了
|
||||||
|
云想:宋谨告诉我的...
|
||||||
|
程澈:我没有故意隐瞒,只是不想你有压力
|
||||||
|
|
||||||
|
△程澈走近云想
|
||||||
|
|
||||||
|
程澈:云想,我想问你一个问题
|
||||||
|
云想:什么?
|
||||||
|
程澈:如果我不是程氏集团的少爷,只是一个普通学生,你还会接受我的保护吗?
|
||||||
|
|
||||||
|
△云想愣住,看着程澈认真的眼神
|
||||||
|
|
||||||
|
云想:为什么这么问?
|
||||||
|
程澈:因为我希望你喜欢的是我这个人,而不是我的身份
|
||||||
|
|
||||||
|
△云想心中一暖
|
||||||
|
|
||||||
|
云想:程澈,你为什么要保护我?
|
||||||
|
程澈(深情):因为从第一眼看到你,我就知道,我不能让任何人伤害你
|
||||||
|
|
||||||
|
△两人四目相对,空气中弥漫着暧昧
|
||||||
|
|
||||||
|
程澈:云想,做我女朋友好吗?
|
||||||
|
|
||||||
|
△云想震惊,脸红得像苹果
|
||||||
|
|
||||||
|
云想:我...我...
|
||||||
|
程澈(温柔):不用急着回答,我等你
|
||||||
|
|
||||||
|
【第3集结尾钩子 - 危机再现】
|
||||||
|
|
||||||
|
3-4 场 夜 内 云想房间
|
||||||
|
人物:云想
|
||||||
|
|
||||||
|
△云想躺在床上,回想着程澈的表白
|
||||||
|
△手机突然响起,是陌生号码
|
||||||
|
|
||||||
|
神秘声音:云想,考虑清楚了吗?
|
||||||
|
云想:你到底是谁?想要什么?
|
||||||
|
神秘声音:我要的很简单,离开程澈
|
||||||
|
云想:为什么?
|
||||||
|
神秘声音:因为你的存在,会害死他
|
||||||
|
|
||||||
|
△电话挂断,云想惊恐地坐起来
|
||||||
|
|
||||||
|
云想(OS):害死他?这是什么意思?
|
||||||
|
|
||||||
|
△窗外突然传来摩托车声
|
||||||
|
△云想看向窗外,程澈正在楼下等她
|
||||||
|
△程澈抬头看向她的窗户,温柔地挥手
|
||||||
|
△云想内心挣扎,既想跑下去,又害怕会害了他
|
||||||
|
|
||||||
|
【第3集完】
|
||||||
|
|
||||||
|
==== 创作说明 ====
|
||||||
|
|
||||||
|
【改编亮点】
|
||||||
|
1. **开篇即高潮**:第1集直接从霸凌事件开始,3秒抓住观众
|
||||||
|
2. **人设极致化**:程澈从温和学霸变成霸道护妻狂魔
|
||||||
|
3. **爽点密集**:每集至少2个打脸/宠溺场面
|
||||||
|
4. **悬念设置**:神秘身份+威胁短信+匿名电话层层递进
|
||||||
|
5. **情感升温**:从保护到表白,节奏紧凑
|
||||||
|
|
||||||
|
【商业化考量】
|
||||||
|
1. **完播率**:每集结尾都有强烈钩子
|
||||||
|
2. **付费点**:程澈身份揭晓、表白场面、危机解决
|
||||||
|
3. **话题性**:霸总护妻、校园霸凌、身份悬疑
|
||||||
|
4. **代入感**:云想从被欺凌到被宠爱的逆袭
|
||||||
|
|
||||||
|
【与原著对比】
|
||||||
|
- 原著27集内容压缩至3集
|
||||||
|
- 删除大量日常铺垫,直奔主题
|
||||||
|
- 强化冲突对立,增加戏剧张力
|
||||||
|
- 人物关系更加极致化和标签化
|
||||||
491
doc/参考/第10-12集剧本.txt
Normal file
491
doc/参考/第10-12集剧本.txt
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
【第10-12集剧本 - 终极对决,完美结局】
|
||||||
|
|
||||||
|
==== 第10集:身份揭秘,终极对决 ====
|
||||||
|
|
||||||
|
【神秘敌人现身】
|
||||||
|
|
||||||
|
10-1 场 日 内 程氏集团董事会
|
||||||
|
人物:程澈、程枭、董事们、神秘人(程澈的叔叔程威)
|
||||||
|
|
||||||
|
△程澈正在主持董事会
|
||||||
|
△突然,一个中年男子走进会议室
|
||||||
|
|
||||||
|
程威:各位董事,不好意思打扰了
|
||||||
|
程澈(震惊):二叔?你不是在国外吗?
|
||||||
|
程威:我回来了,而且带来了一个好消息
|
||||||
|
程枭:什么好消息?
|
||||||
|
程威:我要重新夺回程氏集团的控制权
|
||||||
|
|
||||||
|
△全场哗然
|
||||||
|
|
||||||
|
程澈:二叔,你在说什么?
|
||||||
|
程威:澈儿,你还太年轻,不适合管理这么大的企业
|
||||||
|
程澈:是你在背后搞鬼?
|
||||||
|
程威:包括陈总的事情,都是我安排的
|
||||||
|
|
||||||
|
△程威拿出一份文件
|
||||||
|
|
||||||
|
程威:这是你父亲当年的遗嘱,真正的遗嘱
|
||||||
|
程澈:不可能!
|
||||||
|
程威:上面写得很清楚,如果你在25岁之前结婚,就失去继承权
|
||||||
|
|
||||||
|
【云想的抉择】
|
||||||
|
|
||||||
|
10-2 场 日 内 学校教室
|
||||||
|
人物:云想、观鹤、宋谨
|
||||||
|
|
||||||
|
△宋谨匆忙找到云想
|
||||||
|
|
||||||
|
宋谨:想想,澈哥出事了!
|
||||||
|
云想:什么事?
|
||||||
|
宋谨:他的叔叔回来了,要夺走程氏集团
|
||||||
|
观鹤:怎么会这样?
|
||||||
|
宋谨:而且,如果澈哥结婚,就会失去一切
|
||||||
|
|
||||||
|
△云想脸色苍白
|
||||||
|
|
||||||
|
云想:你的意思是,如果我和程澈在一起,他就会失去所有?
|
||||||
|
宋谨:是的
|
||||||
|
云想:那我不能害他
|
||||||
|
观鹤:想想,你要做什么?
|
||||||
|
云想:我要离开他
|
||||||
|
|
||||||
|
【程澈的坚持】
|
||||||
|
|
||||||
|
10-3 场 黄昏 外 学校天台
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△程澈找到准备离开的云想
|
||||||
|
|
||||||
|
程澈:云想,你要去哪里?
|
||||||
|
云想:程澈,我们分手吧
|
||||||
|
程澈:为什么?
|
||||||
|
云想:因为我不能让你为了我失去一切
|
||||||
|
程澈:云想,你听我说
|
||||||
|
云想:不,程澈。程氏集团是你父亲的心血,你不能因为我而失去它
|
||||||
|
|
||||||
|
△程澈紧紧抱住云想
|
||||||
|
|
||||||
|
程澈:云想,没有你,我要那些财产有什么用?
|
||||||
|
云想:程澈,你要理智一点
|
||||||
|
程澈:我很理智。我的理智告诉我,你比任何财产都重要
|
||||||
|
云想(流泪):可是...
|
||||||
|
程澈:云想,相信我,我会找到解决办法的
|
||||||
|
|
||||||
|
【程威的威胁】
|
||||||
|
|
||||||
|
10-4 场 夜 内 程威办公室
|
||||||
|
人物:程威、云想
|
||||||
|
|
||||||
|
△云想主动来找程威
|
||||||
|
|
||||||
|
程威:云想小姐,欢迎
|
||||||
|
云想:程叔叔,我想和您谈谈
|
||||||
|
程威:我知道你想说什么
|
||||||
|
云想:请您放过程澈
|
||||||
|
程威:这很简单,只要你离开他
|
||||||
|
云想:如果我离开,您就不会夺走程氏集团?
|
||||||
|
程威:当然,我这样做,也是为了澈儿好
|
||||||
|
|
||||||
|
△程威拿出一张支票
|
||||||
|
|
||||||
|
程威:这是五千万,够你下半辈子花了
|
||||||
|
云想:我不要钱
|
||||||
|
程威:那你要什么?
|
||||||
|
云想:我要程澈幸福
|
||||||
|
程威:那就离开他
|
||||||
|
|
||||||
|
【真相大白】
|
||||||
|
|
||||||
|
10-5 场 夜 内 程澈房间
|
||||||
|
人物:程澈、宋谨、私家侦探
|
||||||
|
|
||||||
|
△私家侦探带来了调查结果
|
||||||
|
|
||||||
|
侦探:程少爷,我查到了真相
|
||||||
|
程澈:说
|
||||||
|
侦探:那份遗嘱是假的
|
||||||
|
程澈:什么?
|
||||||
|
侦探:您父亲的真正遗嘱在这里
|
||||||
|
|
||||||
|
△侦探拿出真正的遗嘱
|
||||||
|
|
||||||
|
侦探:上面写着,无论您是否结婚,都拥有完全的继承权
|
||||||
|
宋谨:那程威为什么要这样做?
|
||||||
|
侦探:因为他欠了巨额赌债,需要钱
|
||||||
|
|
||||||
|
△程澈愤怒地握紧拳头
|
||||||
|
|
||||||
|
程澈:他居然敢欺骗我!
|
||||||
|
宋谨:澈哥,现在怎么办?
|
||||||
|
程澈:明天的董事会,我要揭穿他的真面目
|
||||||
|
|
||||||
|
【第10集结尾钩子】
|
||||||
|
|
||||||
|
10-6 场 深夜 外 机场
|
||||||
|
人物:云想、程威的手下
|
||||||
|
|
||||||
|
△云想拖着行李箱准备离开
|
||||||
|
△程威的手下出现
|
||||||
|
|
||||||
|
手下:云想小姐,程威先生有话要转达
|
||||||
|
云想:什么话?
|
||||||
|
手下:如果你不离开,程澈就会出事
|
||||||
|
云想:你们想做什么?
|
||||||
|
手下:程威先生说,明天的董事会,程澈可能会遇到意外
|
||||||
|
|
||||||
|
△云想脸色大变
|
||||||
|
|
||||||
|
云想:我知道了
|
||||||
|
|
||||||
|
△云想拿出手机,给程澈发了最后一条短信
|
||||||
|
△短信内容:"程澈,对不起,我必须离开。请你保重。"
|
||||||
|
|
||||||
|
【第10集完】
|
||||||
|
|
||||||
|
==== 第11集:绝地反击,爱情胜利 ====
|
||||||
|
|
||||||
|
【董事会摊牌】
|
||||||
|
|
||||||
|
11-1 场 日 内 程氏集团董事会
|
||||||
|
人物:程澈、程威、程枭、董事们、律师
|
||||||
|
|
||||||
|
△程澈带着律师和证据出现在董事会
|
||||||
|
|
||||||
|
程澈:各位董事,今天我要揭露一个真相
|
||||||
|
程威:澈儿,你要做什么?
|
||||||
|
程澈:我要揭露你的谎言
|
||||||
|
|
||||||
|
△程澈拿出真正的遗嘱
|
||||||
|
|
||||||
|
程澈:这是我父亲真正的遗嘱
|
||||||
|
程威(慌张):不可能!
|
||||||
|
律师:经过专业鉴定,这份遗嘱是真实有效的
|
||||||
|
程澈:上面明确写着,我拥有程氏集团的完全继承权
|
||||||
|
|
||||||
|
△董事们议论纷纷
|
||||||
|
|
||||||
|
董事A:那之前的遗嘱是假的?
|
||||||
|
程澈:是的,而且我还查到了程威伪造遗嘱的证据
|
||||||
|
|
||||||
|
△程澈播放录音,是程威和伪造者的对话
|
||||||
|
|
||||||
|
程威(录音):一定要做得逼真一点,不能让澈儿发现
|
||||||
|
|
||||||
|
△全场震惊
|
||||||
|
|
||||||
|
程枭:程威,你怎么能这样做?
|
||||||
|
程威:我...我是为了澈儿好
|
||||||
|
程澈:为了我好?你是为了还赌债!
|
||||||
|
|
||||||
|
【程威的末路】
|
||||||
|
|
||||||
|
11-2 场 日 内 程氏集团董事会(续)
|
||||||
|
|
||||||
|
△程澈继续揭露程威的罪行
|
||||||
|
|
||||||
|
程澈:程威不仅伪造遗嘱,还威胁我的女朋友
|
||||||
|
程威:我没有!
|
||||||
|
程澈:宋谨,把录音放出来
|
||||||
|
|
||||||
|
△播放程威威胁云想的录音
|
||||||
|
|
||||||
|
程威(录音):如果你不离开程澈,他就会出事
|
||||||
|
|
||||||
|
△董事们愤怒了
|
||||||
|
|
||||||
|
董事B:程威,你太过分了!
|
||||||
|
董事C:这种人不配留在程氏集团!
|
||||||
|
程枭:程威,你让我太失望了
|
||||||
|
|
||||||
|
△程威知道大势已去
|
||||||
|
|
||||||
|
程威:好,我承认,我做了这些事
|
||||||
|
程澈:为什么?
|
||||||
|
程威:因为我嫉妒你!嫉妒你拥有一切!
|
||||||
|
程澈:二叔,你本来也可以拥有这些
|
||||||
|
程威:但是你父亲只信任你!
|
||||||
|
|
||||||
|
△保安进来带走程威
|
||||||
|
|
||||||
|
程威(被带走时):程澈,你会后悔的!
|
||||||
|
|
||||||
|
【寻找云想】
|
||||||
|
|
||||||
|
11-3 场 日 外 机场
|
||||||
|
人物:程澈、宋谨、观鹤
|
||||||
|
|
||||||
|
△程澈疯狂寻找云想
|
||||||
|
|
||||||
|
程澈:她的航班是几点?
|
||||||
|
宋谨:下午三点,飞往巴黎
|
||||||
|
观鹤:现在已经两点半了
|
||||||
|
程澈:来得及!
|
||||||
|
|
||||||
|
△程澈狂奔向登机口
|
||||||
|
△但是被安检拦住
|
||||||
|
|
||||||
|
安检:先生,您没有机票不能进去
|
||||||
|
程澈:求求你,让我进去找我的女朋友
|
||||||
|
安检:抱歉,这是规定
|
||||||
|
|
||||||
|
△程澈绝望地看着登机口
|
||||||
|
|
||||||
|
【云想的回心转意】
|
||||||
|
|
||||||
|
11-4 场 日 内 飞机上
|
||||||
|
人物:云想、空姐、乘客
|
||||||
|
|
||||||
|
△云想坐在飞机上,眼中含泪
|
||||||
|
△飞机准备起飞
|
||||||
|
△突然,她听到机场广播
|
||||||
|
|
||||||
|
广播:云想小姐,云想小姐,程澈在机场等您,请您下飞机
|
||||||
|
|
||||||
|
△云想震惊地站起来
|
||||||
|
|
||||||
|
空姐:小姐,请坐好,飞机要起飞了
|
||||||
|
云想:不,我要下去
|
||||||
|
空姐:抱歉,飞机已经关舱门了
|
||||||
|
|
||||||
|
△云想看向窗外,看到程澈在跑道上
|
||||||
|
△程澈举着一个大牌子:"云想,我爱你!"
|
||||||
|
|
||||||
|
云想(大喊):停飞机!我要下去!
|
||||||
|
|
||||||
|
△其他乘客被感动
|
||||||
|
|
||||||
|
乘客A:让她下去吧!
|
||||||
|
乘客B:这是真爱啊!
|
||||||
|
|
||||||
|
△机长最终同意让云想下飞机
|
||||||
|
|
||||||
|
【机场重逢】
|
||||||
|
|
||||||
|
11-5 场 日 外 机场跑道
|
||||||
|
人物:程澈、云想、机场工作人员
|
||||||
|
|
||||||
|
△云想从飞机上下来
|
||||||
|
△程澈冲向她
|
||||||
|
|
||||||
|
程澈:云想!
|
||||||
|
云想:程澈!
|
||||||
|
|
||||||
|
△两人紧紧拥抱
|
||||||
|
|
||||||
|
云想:你怎么进来的?
|
||||||
|
程澈:我买了机票,然后又退了
|
||||||
|
云想(笑着哭):你个傻瓜
|
||||||
|
程澈:云想,程威的事情已经解决了
|
||||||
|
云想:真的吗?
|
||||||
|
程澈:真的,我们再也不用分开了
|
||||||
|
|
||||||
|
△程澈单膝跪下,拿出戒指
|
||||||
|
|
||||||
|
程澈:云想,嫁给我吧
|
||||||
|
云想:程澈...
|
||||||
|
程澈:我向你保证,从今以后,没有任何人能够分开我们
|
||||||
|
云想(点头):我愿意!
|
||||||
|
|
||||||
|
△周围的人都在鼓掌
|
||||||
|
△两人深情拥吻
|
||||||
|
|
||||||
|
【第11集结尾钩子】
|
||||||
|
|
||||||
|
11-6 场 夜 内 程澈房间
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△两人相拥而眠
|
||||||
|
△程澈的手机响起
|
||||||
|
△是一条新闻推送:"程氏集团少爷机场求婚成功,网友直呼太浪漫"
|
||||||
|
|
||||||
|
△程澈看着新闻,微笑
|
||||||
|
|
||||||
|
程澈(OS):云想,我们的故事才刚刚开始
|
||||||
|
|
||||||
|
【第11集完】
|
||||||
|
|
||||||
|
==== 第12集:幸福结局,爱情永恒 ====
|
||||||
|
|
||||||
|
【婚礼筹备】
|
||||||
|
|
||||||
|
12-1 场 日 内 婚纱店
|
||||||
|
人物:云想、观鹤、程澈、宋谨
|
||||||
|
|
||||||
|
△云想在试婚纱
|
||||||
|
△观鹤在旁边帮忙
|
||||||
|
|
||||||
|
观鹤:想想,你穿这件太美了!
|
||||||
|
云想:真的吗?
|
||||||
|
程澈(痴迷地看着):美得像天使
|
||||||
|
宋谨:澈哥,你的口水要流出来了
|
||||||
|
|
||||||
|
△大家都笑了
|
||||||
|
|
||||||
|
云想:程澈,你觉得这件怎么样?
|
||||||
|
程澈:只要是你穿的,都是最美的
|
||||||
|
观鹤:澈哥,你这个回答太标准了
|
||||||
|
程澈:因为这是我的真心话
|
||||||
|
|
||||||
|
【家人祝福】
|
||||||
|
|
||||||
|
12-2 场 日 内 程家客厅
|
||||||
|
人物:程澈、云想、程枭、云想父母
|
||||||
|
|
||||||
|
△两家人聚在一起讨论婚礼
|
||||||
|
|
||||||
|
程枭:云想,欢迎你成为我们程家的一员
|
||||||
|
云想:谢谢爷爷
|
||||||
|
云想妈妈:澈儿,想想就交给你了
|
||||||
|
程澈:阿姨,我会用生命保护她的
|
||||||
|
云想爸爸:澈儿,我们相信你
|
||||||
|
|
||||||
|
△程枭拿出一个盒子
|
||||||
|
|
||||||
|
程枭:这是程家的传家宝,现在传给你们
|
||||||
|
云想:爷爷,这太贵重了
|
||||||
|
程枭:你是程家的媳妇,这是你应得的
|
||||||
|
|
||||||
|
【婚礼现场】
|
||||||
|
|
||||||
|
12-3 场 日 外 教堂
|
||||||
|
人物:程澈、云想、所有亲友
|
||||||
|
|
||||||
|
△豪华的婚礼现场
|
||||||
|
△程澈穿着白色西装,帅气逼人
|
||||||
|
△云想穿着白色婚纱,美若天仙
|
||||||
|
|
||||||
|
牧师:程澈先生,你愿意娶云想小姐为妻吗?
|
||||||
|
程澈:我愿意
|
||||||
|
牧师:云想小姐,你愿意嫁给程澈先生为妻吗?
|
||||||
|
云想:我愿意
|
||||||
|
|
||||||
|
△两人交换戒指
|
||||||
|
|
||||||
|
牧师:现在,你们可以亲吻了
|
||||||
|
|
||||||
|
△程澈掀起云想的头纱,深情凝视
|
||||||
|
|
||||||
|
程澈:云想,我爱你
|
||||||
|
云想:我也爱你,程澈
|
||||||
|
|
||||||
|
△两人深情拥吻
|
||||||
|
△全场掌声雷动
|
||||||
|
|
||||||
|
【幸福时光】
|
||||||
|
|
||||||
|
12-4 场 日 外 海边别墅
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△蜜月旅行,两人在海边别墅
|
||||||
|
△云想在沙滩上奔跑
|
||||||
|
△程澈在后面追
|
||||||
|
|
||||||
|
程澈:云想,等等我!
|
||||||
|
云想:追不到我!
|
||||||
|
|
||||||
|
△程澈追上云想,两人一起倒在沙滩上
|
||||||
|
|
||||||
|
云想:程澈,你说我们会一直这么幸福吗?
|
||||||
|
程澈:会的,我保证
|
||||||
|
云想:如果有一天你不爱我了怎么办?
|
||||||
|
程澈:那就是世界末日了
|
||||||
|
|
||||||
|
△云想笑着打程澈
|
||||||
|
|
||||||
|
云想:你就会说好听的
|
||||||
|
程澈:因为你值得世界上最好听的话
|
||||||
|
|
||||||
|
【一年后】
|
||||||
|
|
||||||
|
12-5 场 日 内 医院
|
||||||
|
人物:程澈、云想、医生
|
||||||
|
|
||||||
|
△云想在做产检
|
||||||
|
△程澈紧张地陪在身边
|
||||||
|
|
||||||
|
医生:恭喜你们,是个健康的男孩
|
||||||
|
程澈(激动):真的吗?
|
||||||
|
医生:是的,预产期是下个月
|
||||||
|
云想:程澈,我们要当爸爸妈妈了
|
||||||
|
程澈:云想,谢谢你给我一个完整的家
|
||||||
|
|
||||||
|
△程澈轻抚云想的肚子
|
||||||
|
|
||||||
|
程澈:宝宝,爸爸爱你
|
||||||
|
云想:他在踢我,好像在回应你
|
||||||
|
程澈:他一定是个聪明的孩子
|
||||||
|
云想:像你一样聪明,像我一样善良
|
||||||
|
|
||||||
|
【完美结局】
|
||||||
|
|
||||||
|
12-6 场 日 外 程家花园
|
||||||
|
人物:程澈、云想、宝宝、程枭、双方父母、观鹤、宋谨
|
||||||
|
|
||||||
|
△三年后,程家花园里举办宝宝的生日派对
|
||||||
|
△小程澈(宝宝)在草地上玩耍
|
||||||
|
|
||||||
|
小程澈:爸爸,抱抱!
|
||||||
|
程澈:来,爸爸抱
|
||||||
|
|
||||||
|
△程澈抱起儿子
|
||||||
|
△云想在旁边微笑
|
||||||
|
|
||||||
|
云想:程澈,你还记得我们第一次见面吗?
|
||||||
|
程澈:当然记得,你撞了我的车
|
||||||
|
云想:那时候我怎么也想不到,会和你结婚生子
|
||||||
|
程澈:这就是命运
|
||||||
|
|
||||||
|
△观鹤走过来
|
||||||
|
|
||||||
|
观鹤:想想,你们太幸福了,我都嫉妒了
|
||||||
|
云想:观鹤,你的幸福也会来的
|
||||||
|
宋谨:是啊,我已经给观鹤介绍了好几个男朋友了
|
||||||
|
观鹤:宋谨!
|
||||||
|
|
||||||
|
△大家都笑了
|
||||||
|
|
||||||
|
程枭:澈儿,云想,看到你们这么幸福,我就放心了
|
||||||
|
程澈:爷爷,这都是您的功劳
|
||||||
|
程枭:不,这是你们自己争取来的
|
||||||
|
|
||||||
|
△夕阳西下,一家人其乐融融
|
||||||
|
|
||||||
|
云想(OS):有时候我会想,如果当初没有撞到程澈的车,我们还会相遇吗?
|
||||||
|
程澈(OS):不管怎样,我们都会相遇的。因为你是我命中注定的人
|
||||||
|
|
||||||
|
△镜头拉远,温馨的画面定格
|
||||||
|
|
||||||
|
【字幕】:爱情不是偶然,而是必然。当两个相爱的人走到一起,全世界都会为他们让路。
|
||||||
|
|
||||||
|
【全剧终】
|
||||||
|
|
||||||
|
==== 创作说明 ====
|
||||||
|
|
||||||
|
【第10-12集改编亮点】
|
||||||
|
1. **终极反转**:程威身份揭秘,增加戏剧张力
|
||||||
|
2. **机场追爱**:经典浪漫桥段,满足观众期待
|
||||||
|
3. **完美结局**:婚礼、生子、家庭和睦的圆满收官
|
||||||
|
4. **情感升华**:从恋爱到婚姻到家庭的完整弧线
|
||||||
|
5. **商业闭环**:所有矛盾解决,给观众满足感
|
||||||
|
|
||||||
|
【情绪设计】
|
||||||
|
1. **紧张刺激**:董事会对决、机场追逐
|
||||||
|
2. **浪漫甜蜜**:求婚、婚礼、蜜月
|
||||||
|
3. **温馨感人**:家庭和睦、生儿育女
|
||||||
|
4. **圆满结局**:所有角色都有好的归宿
|
||||||
|
|
||||||
|
【商业价值】
|
||||||
|
1. **话题性**:机场求婚、豪华婚礼等热门话题
|
||||||
|
2. **情绪满足**:完美结局给观众最大的情绪满足
|
||||||
|
3. **二创素材**:婚礼、求婚等场景适合短视频传播
|
||||||
|
4. **IP延展**:为可能的续集或衍生剧留下空间
|
||||||
|
|
||||||
|
【与原著对比】
|
||||||
|
- 大幅压缩时间线,快速推进到结局
|
||||||
|
- 增加家族斗争元素,提升戏剧性
|
||||||
|
- 强化浪漫元素,满足观众期待
|
||||||
|
- 完善所有角色的结局,增加完整性
|
||||||
|
|
||||||
|
【整体总结】
|
||||||
|
《好想你知道》改编版成功将原著的温馨校园恋爱故事,魔改为节奏紧凑、情节密集、爽点频出的霸总宠妻短剧。通过人设极致化、情节戏剧化、节奏快速化的改编策略,打造出具有强烈网感和商业价值的爆款短剧。
|
||||||
452
doc/参考/第4-6集剧本.txt
Normal file
452
doc/参考/第4-6集剧本.txt
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
【第4-6集剧本 - 关系升温,外部冲突】
|
||||||
|
|
||||||
|
==== 第4集:确立关系,身份曝光 ====
|
||||||
|
|
||||||
|
【开场回应表白】
|
||||||
|
|
||||||
|
4-1 场 晨 外 学校门口
|
||||||
|
人物:云想、程澈、围观学生
|
||||||
|
|
||||||
|
△云想在学校门口等程澈
|
||||||
|
△程澈骑摩托车出现,看到云想眼睛一亮
|
||||||
|
|
||||||
|
程澈(下车):等很久了?
|
||||||
|
云想(脸红):没有...程澈,关于昨天你说的话...
|
||||||
|
程澈(紧张):嗯?
|
||||||
|
云想(鼓起勇气):我...我愿意
|
||||||
|
|
||||||
|
△程澈愣住,随即露出从未有过的温柔笑容
|
||||||
|
|
||||||
|
程澈:真的?
|
||||||
|
云想(点头):真的
|
||||||
|
|
||||||
|
△程澈激动地将云想拉入怀中
|
||||||
|
△围观学生纷纷拍照,议论纷纷
|
||||||
|
|
||||||
|
学生甲:天哪!程澈居然笑了!
|
||||||
|
学生乙:他们在一起了!
|
||||||
|
学生丙:这是什么神仙爱情!
|
||||||
|
|
||||||
|
程澈(对云想):从今天开始,你就是我的女朋友了
|
||||||
|
云想(害羞):嗯...
|
||||||
|
程澈:我会让全世界都知道,你是我程澈的女人
|
||||||
|
|
||||||
|
【宠妻模式全面升级】
|
||||||
|
|
||||||
|
4-2 场 日 内 教室
|
||||||
|
人物:程澈、云想、全班同学、班主任
|
||||||
|
|
||||||
|
△上课时间,程澈突然站起来
|
||||||
|
|
||||||
|
程澈:老师,我有话要说
|
||||||
|
班主任:程澈,现在是上课时间...
|
||||||
|
程澈:很重要的事
|
||||||
|
|
||||||
|
△程澈走到云想身边,当众牵起她的手
|
||||||
|
|
||||||
|
程澈(对全班):我正式宣布,云想是我的女朋友。以后谁敢欺负她,就是跟我程澈过不去
|
||||||
|
|
||||||
|
△全班哗然,云想脸红得像苹果
|
||||||
|
△班主任也震惊得说不出话
|
||||||
|
|
||||||
|
程澈(继续):另外,从今天开始,云想的一切费用我来承担。她不需要任何人的同情和施舍
|
||||||
|
|
||||||
|
△程澈拿出一张黑卡,放在云想桌上
|
||||||
|
|
||||||
|
程澈:这张卡没有限额,想买什么就买什么
|
||||||
|
云想(推辞):程澈,这太贵重了...
|
||||||
|
程澈(霸道):我的女人,就应该被宠着
|
||||||
|
|
||||||
|
【林雅反击 - 外部冲突升级】
|
||||||
|
|
||||||
|
4-3 场 日 外 学校天台
|
||||||
|
人物:林雅、云想、林雅的跟班
|
||||||
|
|
||||||
|
△云想独自在天台吃饭
|
||||||
|
△林雅带着几个跟班出现
|
||||||
|
|
||||||
|
林雅:云想,我们该好好谈谈了
|
||||||
|
云想(警惕):林雅,你想干什么?
|
||||||
|
林雅:我想让你认清现实
|
||||||
|
|
||||||
|
△林雅拿出一叠照片,扔在云想面前
|
||||||
|
△照片内容:云想在孤儿院的生活照片
|
||||||
|
|
||||||
|
林雅:看看你的出身,再看看程澈的身份。你觉得你们合适吗?
|
||||||
|
云想(愤怒):出身不能决定一切!
|
||||||
|
林雅(冷笑):是吗?那你知道程澈的真实身份吗?
|
||||||
|
|
||||||
|
△林雅拿出平板,播放一段视频
|
||||||
|
△视频内容:程澈在商业会议上的画面,被称为"程少爷"
|
||||||
|
|
||||||
|
林雅:程氏集团未来的继承人,身价百亿。而你,一个孤儿院出来的野丫头
|
||||||
|
云想(咬牙):程澈不在乎这些
|
||||||
|
林雅:他现在不在乎,不代表以后不在乎。等他玩腻了,你觉得他还会要你吗?
|
||||||
|
|
||||||
|
△云想被说中痛处,眼中含泪
|
||||||
|
|
||||||
|
林雅(得意):识相的话,主动离开他。否则,我有一万种方法让你在这个学校待不下去
|
||||||
|
|
||||||
|
【程澈护妻 - 霸道宣言】
|
||||||
|
|
||||||
|
4-4 场 日 外 学校天台(接上场)
|
||||||
|
人物:程澈、云想、林雅、林雅跟班
|
||||||
|
|
||||||
|
△程澈突然出现,脸色阴沉
|
||||||
|
|
||||||
|
程澈:林雅,你在干什么?
|
||||||
|
林雅(撒娇):澈哥哥,我只是在跟妹妹聊天
|
||||||
|
程澈(看向云想):她说了什么?
|
||||||
|
|
||||||
|
△云想摇头,不想惹麻烦
|
||||||
|
△程澈看到地上的照片,脸色更加难看
|
||||||
|
|
||||||
|
程澈(冷声):这些照片哪来的?
|
||||||
|
林雅:我只是想让她认清现实...
|
||||||
|
程澈(打断):认清什么现实?
|
||||||
|
|
||||||
|
△程澈走到云想身边,将她拉入怀中
|
||||||
|
|
||||||
|
程澈(对林雅):我最后警告你一次,不许再骚扰我的女人
|
||||||
|
林雅:澈哥哥,我们从小一起长大,你为了一个外人...
|
||||||
|
程澈(冷笑):外人?她是我选择的人,是我要用一生去保护的人
|
||||||
|
|
||||||
|
△程澈转身对云想
|
||||||
|
|
||||||
|
程澈:云想,不管别人怎么说,你都要记住,在我心里,你比任何人都重要
|
||||||
|
云想(感动):程澈...
|
||||||
|
程澈:我不在乎你的出身,我只在乎你这个人
|
||||||
|
|
||||||
|
△程澈当众吻了云想的额头
|
||||||
|
△林雅气得脸色发白,带着跟班离开
|
||||||
|
|
||||||
|
【第4集结尾钩子】
|
||||||
|
|
||||||
|
4-5 场 夜 内 程澈房间
|
||||||
|
人物:程澈、程枭(电话)
|
||||||
|
|
||||||
|
△程澈接到父亲的电话
|
||||||
|
|
||||||
|
程枭(电话):小澈,听说你在学校公开了和云想的关系?
|
||||||
|
程澈:是的
|
||||||
|
程枭:你知道这意味着什么吗?
|
||||||
|
程澈:什么意思?
|
||||||
|
程枭:林家那边已经知道了,他们很不高兴
|
||||||
|
程澈:我不在乎
|
||||||
|
程枭:但是我在乎!你忘了我们和林家的合作了吗?
|
||||||
|
程澈:爸,我不会为了商业利益牺牲我的感情
|
||||||
|
程枭:程澈,你还年轻,不懂事。这件事到此为止
|
||||||
|
程澈:如果您要我在云想和家族利益之间选择,我选择云想
|
||||||
|
|
||||||
|
△程澈挂断电话,表情坚决
|
||||||
|
|
||||||
|
程澈(OS):不管付出什么代价,我都不会放弃云想
|
||||||
|
|
||||||
|
【第4集完】
|
||||||
|
|
||||||
|
==== 第5集:家族压力,感情考验 ====
|
||||||
|
|
||||||
|
【家族施压】
|
||||||
|
|
||||||
|
5-1 场 日 内 程氏集团董事长办公室
|
||||||
|
人物:程枭、林董事长、程澈
|
||||||
|
|
||||||
|
△程澈被叫到父亲的办公室
|
||||||
|
△林董事长也在场,脸色不善
|
||||||
|
|
||||||
|
程枭:小澈,林叔叔有话要跟你说
|
||||||
|
林董事长:程澈,你和我女儿从小就有婚约
|
||||||
|
程澈:那是你们大人的决定,我从来没有同意过
|
||||||
|
林董事长:现在你为了一个来路不明的女孩,要毁掉两家的合作?
|
||||||
|
程澈:云想不是来路不明,她是我选择的人
|
||||||
|
|
||||||
|
△林董事长拍桌而起
|
||||||
|
|
||||||
|
林董事长:程枭,你就是这么教育儿子的?
|
||||||
|
程枭(为难):小澈,你先回去,这件事我们再商量
|
||||||
|
程澈:没什么好商量的。我不会娶林雅,也不会放弃云想
|
||||||
|
|
||||||
|
△程澈转身要走
|
||||||
|
|
||||||
|
林董事长:程澈,如果你坚持这样,我们林家就要重新考虑和程氏的合作了
|
||||||
|
程澈(回头):那是您的选择
|
||||||
|
|
||||||
|
【云想的自我怀疑】
|
||||||
|
|
||||||
|
5-2 场 日 内 学校图书馆
|
||||||
|
人物:云想、观鹤
|
||||||
|
|
||||||
|
△云想独自在图书馆学习,心不在焉
|
||||||
|
△观鹤坐到她对面
|
||||||
|
|
||||||
|
观鹤:想想,你看起来心情不好
|
||||||
|
云想:观鹤,你说我和程澈真的合适吗?
|
||||||
|
观鹤:为什么这么问?
|
||||||
|
云想:我们的出身差距太大了,他的家人肯定不会接受我
|
||||||
|
观鹤:想想,真正的爱情不应该被出身束缚
|
||||||
|
云想:可是现实很残酷...
|
||||||
|
观鹤:如果你真的爱他,就应该相信他的选择
|
||||||
|
|
||||||
|
△云想陷入沉思
|
||||||
|
|
||||||
|
观鹤:想想,不管你做什么决定,我都会支持你
|
||||||
|
云想:谢谢你,观鹤
|
||||||
|
|
||||||
|
【程澈的坚持】
|
||||||
|
|
||||||
|
5-3 场 黄昏 外 学校操场
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△程澈找到云想,发现她在哭
|
||||||
|
|
||||||
|
程澈(心疼):怎么了?谁欺负你了?
|
||||||
|
云想:程澈,我们分手吧
|
||||||
|
|
||||||
|
△程澈震惊
|
||||||
|
|
||||||
|
程澈:为什么?
|
||||||
|
云想:我们不合适,你的家人不会接受我的
|
||||||
|
程澈:我不在乎他们的想法
|
||||||
|
云想:但是我在乎!我不想成为你的负担
|
||||||
|
|
||||||
|
△程澈紧紧抱住云想
|
||||||
|
|
||||||
|
程澈:云想,听我说。不管发生什么,我都不会放弃你
|
||||||
|
云想:可是...
|
||||||
|
程澈:没有可是。我程澈说过的话,从来不会反悔
|
||||||
|
|
||||||
|
△程澈捧起云想的脸
|
||||||
|
|
||||||
|
程澈:相信我,好吗?
|
||||||
|
云想(哭着点头):嗯...
|
||||||
|
|
||||||
|
【危机升级 - 绑架事件】
|
||||||
|
|
||||||
|
5-4 场 夜 外 回家路上
|
||||||
|
人物:云想、神秘人、程澈
|
||||||
|
|
||||||
|
△云想独自走在回家的路上
|
||||||
|
△突然被几个黑衣人包围
|
||||||
|
|
||||||
|
黑衣人甲:云想小姐,跟我们走一趟
|
||||||
|
云想(害怕):你们是谁?
|
||||||
|
黑衣人乙:有人想见你
|
||||||
|
|
||||||
|
△云想想要逃跑,被抓住
|
||||||
|
△正在此时,程澈的摩托车声响起
|
||||||
|
|
||||||
|
程澈(冷声):放开她
|
||||||
|
黑衣人甲:程少爷,我们只是奉命行事
|
||||||
|
程澈:奉谁的命?
|
||||||
|
|
||||||
|
△一辆黑色轿车停下,林董事长走出来
|
||||||
|
|
||||||
|
林董事长:是我的命
|
||||||
|
程澈(愤怒):林叔叔,您这是什么意思?
|
||||||
|
林董事长:我要让这个女孩知道,她配不上你
|
||||||
|
|
||||||
|
△程澈挡在云想面前
|
||||||
|
|
||||||
|
程澈:想动她,先过我这关
|
||||||
|
林董事长:程澈,你要为了一个女孩跟我翻脸?
|
||||||
|
程澈:如果您要伤害她,那就是
|
||||||
|
|
||||||
|
【第5集结尾钩子】
|
||||||
|
|
||||||
|
5-5 场 夜 外 回家路上(接上场)
|
||||||
|
|
||||||
|
△对峙中,程澈的手机响起
|
||||||
|
△来电显示:爸爸
|
||||||
|
|
||||||
|
程澈(接电话):什么事?
|
||||||
|
程枭(电话):小澈,立刻回来,出大事了
|
||||||
|
程澈:什么大事?
|
||||||
|
程枭:林家撤资了,公司股价暴跌,董事会要求你立刻和林雅订婚
|
||||||
|
|
||||||
|
△程澈脸色大变
|
||||||
|
△林董事长得意地笑了
|
||||||
|
|
||||||
|
林董事长:程澈,现在你还要坚持吗?
|
||||||
|
|
||||||
|
△云想听到电话内容,眼中闪过痛苦
|
||||||
|
|
||||||
|
云想(对程澈):去吧,公司更重要
|
||||||
|
程澈(坚决):不,你更重要
|
||||||
|
|
||||||
|
△程澈挂断电话,牵起云想的手
|
||||||
|
|
||||||
|
程澈:我们走
|
||||||
|
|
||||||
|
【第5集完】
|
||||||
|
|
||||||
|
==== 第6集:最终选择,爱情胜利 ====
|
||||||
|
|
||||||
|
【董事会摊牌】
|
||||||
|
|
||||||
|
6-1 场 日 内 程氏集团会议室
|
||||||
|
人物:程澈、程枭、各位董事、林董事长
|
||||||
|
|
||||||
|
△紧急董事会召开
|
||||||
|
△程澈被要求出席
|
||||||
|
|
||||||
|
董事甲:程澈,因为你的任性,公司损失惨重
|
||||||
|
董事乙:你必须立刻和林雅订婚,挽回林家的合作
|
||||||
|
程澈:我拒绝
|
||||||
|
|
||||||
|
△会议室一片哗然
|
||||||
|
|
||||||
|
程枭:小澈,你知道你在说什么吗?
|
||||||
|
程澈:我很清楚。我不会为了商业利益出卖我的感情
|
||||||
|
林董事长:程澈,你要为你的选择负责
|
||||||
|
程澈:我会负责,但不是用这种方式
|
||||||
|
|
||||||
|
△程澈站起来,面对所有人
|
||||||
|
|
||||||
|
程澈:各位叔叔,我程澈从小就被培养成程氏的继承人。但是今天,我要告诉你们,如果继承程氏意味着要放弃我爱的人,那我宁可不要
|
||||||
|
|
||||||
|
△全场震惊
|
||||||
|
|
||||||
|
程澈:从今天开始,我放弃程氏集团的继承权
|
||||||
|
程枭:程澈!
|
||||||
|
程澈:爸,对不起。但是我不能违背自己的心
|
||||||
|
|
||||||
|
【云想的成长 - 主动出击】
|
||||||
|
|
||||||
|
6-2 场 日 内 学校教室
|
||||||
|
人物:云想、林雅、全班同学
|
||||||
|
|
||||||
|
△云想主动找到林雅
|
||||||
|
|
||||||
|
云想:林雅,我们谈谈
|
||||||
|
林雅(得意):有什么好谈的?程澈很快就会是我的未婚夫了
|
||||||
|
云想:我不会让你得逞的
|
||||||
|
林雅(冷笑):就凭你?
|
||||||
|
|
||||||
|
△云想拿出手机,播放一段录音
|
||||||
|
△录音内容:林雅指使人威胁云想的对话
|
||||||
|
|
||||||
|
云想:这段录音如果传出去,你觉得程澈会怎么看你?
|
||||||
|
林雅(慌张):你...你想干什么?
|
||||||
|
云想:我要你公开道歉,并且永远不要再骚扰我和程澈
|
||||||
|
|
||||||
|
△全班同学围观,议论纷纷
|
||||||
|
|
||||||
|
云想(大声):我云想虽然出身贫寒,但我有自己的尊严。我不会因为任何人的威胁而放弃我的爱情
|
||||||
|
|
||||||
|
△同学们纷纷鼓掌
|
||||||
|
|
||||||
|
学生甲:云想说得对!
|
||||||
|
学生乙:真正的爱情不应该被门第束缚!
|
||||||
|
学生丙:我们支持你!
|
||||||
|
|
||||||
|
【程澈的惊喜 - 隐藏实力曝光】
|
||||||
|
|
||||||
|
6-3 场 日 内 程氏集团会议室
|
||||||
|
人物:程澈、程枭、董事们、神秘助理
|
||||||
|
|
||||||
|
△正当董事会准备通过决议时,程澈的助理进来
|
||||||
|
|
||||||
|
助理:程少爷,您要的文件
|
||||||
|
程澈:谢谢
|
||||||
|
|
||||||
|
△程澈拿出一叠文件
|
||||||
|
|
||||||
|
程澈:各位,在我放弃继承权之前,我想让你们看看这个
|
||||||
|
|
||||||
|
△程澈将文件分发给每个人
|
||||||
|
△董事们看后纷纷震惊
|
||||||
|
|
||||||
|
董事甲:这...这是什么?
|
||||||
|
程澈:这是我这三年来秘密投资的项目清单
|
||||||
|
董事乙:天哪,这些项目的总价值...
|
||||||
|
程澈:超过程氏集团目前的市值
|
||||||
|
|
||||||
|
△程枭震惊地看着儿子
|
||||||
|
|
||||||
|
程枭:小澈,你什么时候...
|
||||||
|
程澈:爸,您以为我这三年只是在上学吗?我一直在为接管程氏做准备
|
||||||
|
|
||||||
|
△程澈站起来,气场强大
|
||||||
|
|
||||||
|
程澈:林家的撤资确实会让程氏损失,但不会伤筋动骨。相反,我的这些项目,足以让程氏的实力翻倍
|
||||||
|
林董事长(慌张):这不可能...
|
||||||
|
程澈:林叔叔,您可以选择继续合作,也可以选择离开。但是,请不要再威胁我的女人
|
||||||
|
|
||||||
|
【大团圆结局】
|
||||||
|
|
||||||
|
6-4 场 黄昏 外 学校天台
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△程澈找到云想,将她拉入怀中
|
||||||
|
|
||||||
|
程澈:听说你今天很勇敢
|
||||||
|
云想:我不想再做被保护的小女孩了
|
||||||
|
程澈:那你想做什么?
|
||||||
|
云想:我想做能够站在你身边的女人
|
||||||
|
|
||||||
|
△程澈深情地看着云想
|
||||||
|
|
||||||
|
程澈:云想,你知道吗?从第一眼看到你,我就知道,你就是我要找的人
|
||||||
|
云想:为什么?
|
||||||
|
程澈:因为你让我相信,这个世界上还有纯真的美好
|
||||||
|
|
||||||
|
△程澈单膝跪下,拿出一枚戒指
|
||||||
|
|
||||||
|
程澈:云想,虽然我们还年轻,但我想让全世界都知道,你是我程澈这辈子唯一的女人。你愿意接受这枚承诺戒指吗?
|
||||||
|
|
||||||
|
△云想感动得泪流满面
|
||||||
|
|
||||||
|
云想:我愿意
|
||||||
|
|
||||||
|
△程澈为云想戴上戒指,两人拥吻
|
||||||
|
△夕阳西下,两人的身影被拉得很长
|
||||||
|
|
||||||
|
【第6集结尾 - 完美收官】
|
||||||
|
|
||||||
|
6-5 场 夜 内 程家客厅
|
||||||
|
人物:程澈、云想、程枭、程澈母亲
|
||||||
|
|
||||||
|
△程澈带云想回家见父母
|
||||||
|
△程枭和程澈母亲已经在客厅等候
|
||||||
|
|
||||||
|
程枭:想想,对不起,之前的事情是叔叔不对
|
||||||
|
云想:程叔叔,您不用道歉
|
||||||
|
程澈母亲:想想,欢迎你成为我们家的一员
|
||||||
|
|
||||||
|
△程澈母亲拿出一条项链
|
||||||
|
|
||||||
|
程澈母亲:这是程家传了三代的项链,现在它属于你了
|
||||||
|
云想(感动):阿姨,这太贵重了...
|
||||||
|
程澈母亲:你是小澈选择的人,就是我们的女儿
|
||||||
|
|
||||||
|
△程澈握住云想的手
|
||||||
|
|
||||||
|
程澈:从今以后,没有人能够分开我们
|
||||||
|
云想:嗯,我们永远在一起
|
||||||
|
|
||||||
|
△镜头拉远,一家人其乐融融
|
||||||
|
△字幕出现:"真正的爱情,能够战胜一切阻碍"
|
||||||
|
|
||||||
|
【第6集完】
|
||||||
|
|
||||||
|
==== 创作说明 ====
|
||||||
|
|
||||||
|
【第4-6集改编亮点】
|
||||||
|
1. **关系确立**:第4集直接确立恋爱关系,节奏紧凑
|
||||||
|
2. **冲突升级**:家族压力、商业威胁、绑架危机层层递进
|
||||||
|
3. **人物成长**:云想从被动接受保护到主动反击
|
||||||
|
4. **爽点密集**:程澈护妻、实力曝光、完美反击
|
||||||
|
5. **情感升华**:从保护关系升华为平等的爱情
|
||||||
|
|
||||||
|
【商业化设计】
|
||||||
|
1. **付费点**:程澈实力曝光、求婚场面、家族认可
|
||||||
|
2. **话题性**:霸总放弃继承权、灰姑娘逆袭、真爱胜利
|
||||||
|
3. **情绪调动**:甜虐并存,观众情绪过山车
|
||||||
|
4. **代入感**:每个女孩都梦想被这样宠爱
|
||||||
|
|
||||||
|
【与原著对比】
|
||||||
|
- 大幅压缩时间线,删除冗余情节
|
||||||
|
- 强化程澈的霸总属性和保护欲
|
||||||
|
- 增加商业斗争元素,提升戏剧冲突
|
||||||
|
- 云想的成长弧线更加明显和励志
|
||||||
|
- 结局更加圆满,满足观众期待
|
||||||
445
doc/参考/第7-9集剧本.txt
Normal file
445
doc/参考/第7-9集剧本.txt
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
【第7-9集剧本 - 危机考验,感情波折】
|
||||||
|
|
||||||
|
==== 第7集:新的威胁,信任危机 ====
|
||||||
|
|
||||||
|
【神秘敌人出现】
|
||||||
|
|
||||||
|
7-1 场 日 外 咖啡厅
|
||||||
|
人物:云想、神秘男子(陈总)、程澈
|
||||||
|
|
||||||
|
△云想独自在咖啡厅等程澈
|
||||||
|
△一个中年男子坐到她对面
|
||||||
|
|
||||||
|
陈总:云想小姐,我们终于见面了
|
||||||
|
云想(警惕):你是谁?
|
||||||
|
陈总:我姓陈,是程氏集团的竞争对手
|
||||||
|
云想:你想干什么?
|
||||||
|
陈总:我想跟你做个交易
|
||||||
|
|
||||||
|
△陈总拿出一张支票
|
||||||
|
|
||||||
|
陈总:一千万,离开程澈
|
||||||
|
云想(愤怒):你以为我是什么人?
|
||||||
|
陈总:每个人都有价格,只是多少的问题
|
||||||
|
云想:我不会为了钱背叛程澈
|
||||||
|
|
||||||
|
△陈总收起支票,拿出一叠照片
|
||||||
|
|
||||||
|
陈总:那这些呢?
|
||||||
|
|
||||||
|
△照片内容:程澈和其他女孩的亲密照片(PS的)
|
||||||
|
|
||||||
|
陈总:程澈在你之前,可是有很多女朋友的
|
||||||
|
云想(震惊):这些照片...
|
||||||
|
陈总:都是真的。你以为你是他的唯一吗?
|
||||||
|
|
||||||
|
△正在此时,程澈出现
|
||||||
|
|
||||||
|
程澈(冷声):陈总,你在这里做什么?
|
||||||
|
陈总(起身):程少爷,只是偶遇你的女朋友,聊聊天
|
||||||
|
程澈:以后不要再接近她
|
||||||
|
陈总:程少爷,有些事情,瞒得了一时,瞒不了一世
|
||||||
|
|
||||||
|
△陈总离开,留下照片
|
||||||
|
△程澈看到照片,脸色大变
|
||||||
|
|
||||||
|
程澈:云想,这些照片...
|
||||||
|
云想(冷淡):你不用解释
|
||||||
|
程澈:这些都是假的!
|
||||||
|
云想:是吗?那为什么你看到照片会这么紧张?
|
||||||
|
|
||||||
|
【误会加深】
|
||||||
|
|
||||||
|
7-2 场 日 内 学校教室
|
||||||
|
人物:云想、宋谨、观鹤
|
||||||
|
|
||||||
|
△云想心不在焉地上课
|
||||||
|
△宋谨偷偷递纸条给她
|
||||||
|
|
||||||
|
纸条内容:"想想,澈哥找你"
|
||||||
|
|
||||||
|
△云想看了一眼,没有理会
|
||||||
|
△下课后,观鹤走到云想身边
|
||||||
|
|
||||||
|
观鹤:想想,你和程澈吵架了?
|
||||||
|
云想:没有
|
||||||
|
观鹤:但是你看起来很不开心
|
||||||
|
云想:观鹤,你觉得一个人会为了另一个人改变吗?
|
||||||
|
观鹤:如果是真爱的话,会的
|
||||||
|
云想:那如果这个人以前有很多女朋友呢?
|
||||||
|
观鹤:过去不重要,重要的是现在
|
||||||
|
|
||||||
|
△云想陷入沉思
|
||||||
|
|
||||||
|
【程澈的解释】
|
||||||
|
|
||||||
|
7-3 场 黄昏 外 学校天台
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△程澈终于找到云想
|
||||||
|
|
||||||
|
程澈:云想,我们需要谈谈
|
||||||
|
云想:没什么好谈的
|
||||||
|
程澈:关于那些照片,我可以解释
|
||||||
|
云想:你不用解释,我理解
|
||||||
|
程澈:你理解什么?
|
||||||
|
云想:你是程氏集团的少爷,身边有很多女孩很正常
|
||||||
|
|
||||||
|
△程澈急了
|
||||||
|
|
||||||
|
程澈:云想,你听我说,那些照片都是假的!
|
||||||
|
云想:假的?那为什么做得这么逼真?
|
||||||
|
程澈:因为有人想要破坏我们的关系
|
||||||
|
云想:程澈,你觉得我很好骗吗?
|
||||||
|
|
||||||
|
△云想转身要走,程澈拉住她
|
||||||
|
|
||||||
|
程澈:云想,相信我,好吗?
|
||||||
|
云想:程澈,我累了。我们冷静一下吧
|
||||||
|
|
||||||
|
△云想挣脱程澈的手,离开
|
||||||
|
△程澈一个人站在天台上,表情痛苦
|
||||||
|
|
||||||
|
【第7集结尾钩子】
|
||||||
|
|
||||||
|
7-4 场 夜 内 程澈房间
|
||||||
|
人物:程澈、宋谨(电话)
|
||||||
|
|
||||||
|
△程澈独自坐在房间里,看着云想的照片
|
||||||
|
△手机响起,是宋谨的电话
|
||||||
|
|
||||||
|
宋谨(电话):澈哥,我查到了那些照片的来源
|
||||||
|
程澈:说
|
||||||
|
宋谨:是陈氏集团的人做的,他们想要搞垮你
|
||||||
|
程澈:我知道了
|
||||||
|
宋谨:澈哥,你打算怎么办?
|
||||||
|
程澈:我要让他们付出代价
|
||||||
|
|
||||||
|
△程澈眼中闪过危险的光芒
|
||||||
|
|
||||||
|
程澈:敢动我的女人,他们死定了
|
||||||
|
|
||||||
|
【第7集完】
|
||||||
|
|
||||||
|
==== 第8集:危机升级,感情破裂 ====
|
||||||
|
|
||||||
|
【陈总的阴谋】
|
||||||
|
|
||||||
|
8-1 场 日 内 陈氏集团办公室
|
||||||
|
人物:陈总、助理、林雅
|
||||||
|
|
||||||
|
△陈总在办公室里得意地看着监控画面
|
||||||
|
△画面中是程澈和云想冷战的场景
|
||||||
|
|
||||||
|
陈总:很好,他们开始互相怀疑了
|
||||||
|
助理:陈总,下一步怎么办?
|
||||||
|
陈总:继续加码
|
||||||
|
|
||||||
|
△林雅走进办公室
|
||||||
|
|
||||||
|
林雅:陈叔叔,您找我?
|
||||||
|
陈总:雅雅,是时候执行第二阶段计划了
|
||||||
|
林雅:什么计划?
|
||||||
|
陈总:让程澈彻底失去云想
|
||||||
|
|
||||||
|
△陈总拿出一瓶药
|
||||||
|
|
||||||
|
陈总:这是迷药,你想办法让云想喝下去
|
||||||
|
林雅:然后呢?
|
||||||
|
陈总:然后让程澈看到你们在一起的画面
|
||||||
|
林雅(犹豫):这样不太好吧...
|
||||||
|
陈总:你想要程澈,就必须付出代价
|
||||||
|
|
||||||
|
【云想中计】
|
||||||
|
|
||||||
|
8-2 场 日 内 学校咖啡厅
|
||||||
|
人物:云想、林雅
|
||||||
|
|
||||||
|
△云想独自在咖啡厅喝咖啡
|
||||||
|
△林雅主动坐到她对面
|
||||||
|
|
||||||
|
林雅:想想,我们谈谈吧
|
||||||
|
云想:我们没什么好谈的
|
||||||
|
林雅:关于程澈的事情
|
||||||
|
云想:什么?
|
||||||
|
林雅:我知道你们吵架了
|
||||||
|
云想:这不关你的事
|
||||||
|
林雅:想想,其实我们可以做朋友的
|
||||||
|
|
||||||
|
△林雅推了一杯咖啡给云想
|
||||||
|
|
||||||
|
林雅:这是我的道歉,之前的事情是我不对
|
||||||
|
云想(意外):你...
|
||||||
|
林雅:我想通了,如果程澈真的爱你,我也不应该强求
|
||||||
|
|
||||||
|
△云想被感动,喝了咖啡
|
||||||
|
△几分钟后,云想开始感到头晕
|
||||||
|
|
||||||
|
云想:我...我怎么了?
|
||||||
|
林雅(假装关心):想想,你没事吧?我送你去休息
|
||||||
|
|
||||||
|
【程澈误会】
|
||||||
|
|
||||||
|
8-3 场 日 内 学校休息室
|
||||||
|
人物:云想(昏迷)、林雅、程澈
|
||||||
|
|
||||||
|
△林雅将昏迷的云想带到休息室
|
||||||
|
△然后发短信给程澈:"快来休息室,云想出事了"
|
||||||
|
|
||||||
|
△程澈匆忙赶到,看到林雅在照顾云想
|
||||||
|
|
||||||
|
程澈:她怎么了?
|
||||||
|
林雅:不知道,突然就晕倒了
|
||||||
|
程澈(担心):有没有叫医生?
|
||||||
|
林雅:已经叫了
|
||||||
|
|
||||||
|
△程澈想要抱起云想
|
||||||
|
△云想在迷药作用下,迷迷糊糊地推开程澈
|
||||||
|
|
||||||
|
云想(迷糊):不要...不要碰我...
|
||||||
|
程澈(震惊):云想?
|
||||||
|
云想(迷糊):我不想看到你...
|
||||||
|
|
||||||
|
△程澈心如刀割
|
||||||
|
△林雅在旁边假装安慰
|
||||||
|
|
||||||
|
林雅:程澈,也许她真的需要时间冷静
|
||||||
|
程澈:不,她不是这样的人
|
||||||
|
林雅:人在生气的时候,会说很多伤人的话
|
||||||
|
|
||||||
|
【真相部分揭露】
|
||||||
|
|
||||||
|
8-4 场 日 内 医务室
|
||||||
|
人物:云想、校医、程澈、宋谨
|
||||||
|
|
||||||
|
△云想被送到医务室
|
||||||
|
△校医检查后,脸色严肃
|
||||||
|
|
||||||
|
校医:这个学生被下了迷药
|
||||||
|
程澈(愤怒):什么?
|
||||||
|
校医:血液中有迷药成分,幸好剂量不大
|
||||||
|
宋谨:谁会给想想下药?
|
||||||
|
|
||||||
|
△程澈想起林雅的异常表现
|
||||||
|
|
||||||
|
程澈:林雅!
|
||||||
|
|
||||||
|
△程澈冲出医务室,去找林雅
|
||||||
|
△但林雅已经离开学校
|
||||||
|
|
||||||
|
【云想的决定】
|
||||||
|
|
||||||
|
8-5 场 夜 内 云想房间
|
||||||
|
人物:云想、程澈
|
||||||
|
|
||||||
|
△云想醒来后,程澈守在她身边
|
||||||
|
|
||||||
|
程澈:你醒了?感觉怎么样?
|
||||||
|
云想:我...发生了什么?
|
||||||
|
程澈:你被人下了迷药
|
||||||
|
云想(震惊):什么?
|
||||||
|
程澈:是林雅做的
|
||||||
|
云想:为什么?
|
||||||
|
程澈:因为有人想要破坏我们的关系
|
||||||
|
|
||||||
|
△云想沉默了很久
|
||||||
|
|
||||||
|
云想:程澈,我想我们应该分开一段时间
|
||||||
|
程澈:为什么?现在真相已经清楚了
|
||||||
|
云想:正是因为真相清楚了,我才更害怕
|
||||||
|
程澈:害怕什么?
|
||||||
|
云想:害怕因为我,你会遇到更多的危险
|
||||||
|
|
||||||
|
△程澈紧紧抱住云想
|
||||||
|
|
||||||
|
程澈:云想,不要离开我
|
||||||
|
云想(流泪):程澈,我爱你,正是因为爱你,我才不能害你
|
||||||
|
|
||||||
|
【第8集结尾钩子】
|
||||||
|
|
||||||
|
8-6 场 夜 外 云想家楼下
|
||||||
|
人物:云想、神秘电话
|
||||||
|
|
||||||
|
△云想独自站在楼下
|
||||||
|
△手机响起,是陌生号码
|
||||||
|
|
||||||
|
神秘声音:云想,考虑得怎么样?
|
||||||
|
云想:你是谁?
|
||||||
|
神秘声音:一个想要保护程澈的人
|
||||||
|
云想:什么意思?
|
||||||
|
神秘声音:只要你离开他,他就会安全
|
||||||
|
云想:如果我不离开呢?
|
||||||
|
神秘声音:那他就会有生命危险
|
||||||
|
|
||||||
|
△电话挂断,云想脸色苍白
|
||||||
|
|
||||||
|
云想(OS):为了保护程澈,我必须离开他
|
||||||
|
|
||||||
|
【第8集完】
|
||||||
|
|
||||||
|
==== 第9集:生死考验,真爱觉醒 ====
|
||||||
|
|
||||||
|
【云想的牺牲】
|
||||||
|
|
||||||
|
9-1 场 晨 内 云想房间
|
||||||
|
人物:云想
|
||||||
|
|
||||||
|
△云想收拾行李,准备离开
|
||||||
|
△拿起程澈送的戒指,眼中含泪
|
||||||
|
|
||||||
|
云想(OS):程澈,对不起。我这样做,都是为了保护你
|
||||||
|
|
||||||
|
△云想写了一封信,放在桌上
|
||||||
|
△信的内容:"程澈,我们不合适,请忘记我。- 云想"
|
||||||
|
|
||||||
|
【程澈的发现】
|
||||||
|
|
||||||
|
9-2 场 日 内 程澈房间
|
||||||
|
人物:程澈、宋谨
|
||||||
|
|
||||||
|
△程澈发现云想不见了
|
||||||
|
△冲到她的房间,看到信件
|
||||||
|
|
||||||
|
程澈(愤怒):不可能!她不会这样做!
|
||||||
|
宋谨:澈哥,也许她真的...
|
||||||
|
程澈:不!一定有人威胁了她!
|
||||||
|
|
||||||
|
△程澈拿出手机,疯狂拨打云想的号码
|
||||||
|
△但云想的手机已经关机
|
||||||
|
|
||||||
|
程澈:宋谨,动用所有关系,一定要找到她!
|
||||||
|
宋谨:是,澈哥!
|
||||||
|
|
||||||
|
【陈总的最后一击】
|
||||||
|
|
||||||
|
9-3 场 日 内 废弃仓库
|
||||||
|
人物:云想、陈总、手下
|
||||||
|
|
||||||
|
△云想被带到一个废弃仓库
|
||||||
|
△陈总出现在她面前
|
||||||
|
|
||||||
|
陈总:云想小姐,欢迎来到我的地盘
|
||||||
|
云想:你想要什么?
|
||||||
|
陈总:很简单,给程澈打个电话,告诉他你不爱他了
|
||||||
|
云想:我不会的
|
||||||
|
陈总:那就别怪我不客气了
|
||||||
|
|
||||||
|
△陈总的手下逼近云想
|
||||||
|
△云想害怕地后退
|
||||||
|
|
||||||
|
陈总:程澈为了你,已经得罪了很多人。现在,是时候让他付出代价了
|
||||||
|
云想:你们想对程澈做什么?
|
||||||
|
陈总:如果你不配合,他就会出车祸
|
||||||
|
|
||||||
|
△陈总拿出手机,播放程澈开车的实时画面
|
||||||
|
|
||||||
|
陈总:我的人已经在路上等他了
|
||||||
|
云想(恐惧):不要伤害他!
|
||||||
|
陈总:那就配合我
|
||||||
|
|
||||||
|
【程澈的营救】
|
||||||
|
|
||||||
|
9-4 场 日 外 废弃仓库外
|
||||||
|
人物:程澈、宋谨、保镖
|
||||||
|
|
||||||
|
△程澈通过GPS定位找到了云想
|
||||||
|
△带着保镖包围了仓库
|
||||||
|
|
||||||
|
程澈(对讲机):所有人注意,确保云想的安全
|
||||||
|
宋谨:澈哥,陈总的人很多
|
||||||
|
程澈:我不在乎。今天,我一定要救出云想
|
||||||
|
|
||||||
|
△程澈冲进仓库
|
||||||
|
|
||||||
|
【最终对决】
|
||||||
|
|
||||||
|
9-5 场 日 内 废弃仓库
|
||||||
|
人物:程澈、云想、陈总、双方手下
|
||||||
|
|
||||||
|
△程澈出现在仓库里
|
||||||
|
|
||||||
|
程澈:放开她!
|
||||||
|
陈总:程澈,你终于来了
|
||||||
|
程澈:你想要什么?
|
||||||
|
陈总:我要你跪下,求我放过她
|
||||||
|
|
||||||
|
△程澈毫不犹豫地跪下
|
||||||
|
|
||||||
|
程澈:我求你,放过她
|
||||||
|
云想(震惊):程澈,不要!
|
||||||
|
陈总(得意):堂堂程氏集团的少爷,居然为了一个女人下跪
|
||||||
|
程澈:只要你放过她,我什么都愿意做
|
||||||
|
|
||||||
|
△云想被感动得泪流满面
|
||||||
|
|
||||||
|
云想:程澈,你为什么要这样做?
|
||||||
|
程澈:因为我爱你,胜过我的生命
|
||||||
|
|
||||||
|
△正在此时,警察包围了仓库
|
||||||
|
△原来程澈早就报了警
|
||||||
|
|
||||||
|
陈总(慌张):不可能!
|
||||||
|
程澈(站起来):陈总,游戏结束了
|
||||||
|
|
||||||
|
【真爱告白】
|
||||||
|
|
||||||
|
9-6 场 黄昏 外 医院
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△云想在医院接受检查
|
||||||
|
△程澈守在她身边
|
||||||
|
|
||||||
|
云想:程澈,对不起,我不应该不相信你
|
||||||
|
程澈:是我不好,没有保护好你
|
||||||
|
云想:程澈,你为什么愿意为我做这么多?
|
||||||
|
程澈:因为你是我的全世界
|
||||||
|
|
||||||
|
△程澈握住云想的手
|
||||||
|
|
||||||
|
程澈:云想,经过这次的事情,我更加确定,我这辈子只爱你一个人
|
||||||
|
云想:我也是,程澈。我永远不会再离开你
|
||||||
|
|
||||||
|
△两人深情拥吻
|
||||||
|
|
||||||
|
【第9集结尾钩子】
|
||||||
|
|
||||||
|
9-7 场 夜 内 程澈房间
|
||||||
|
人物:程澈、云想
|
||||||
|
|
||||||
|
△两人相拥而眠
|
||||||
|
△程澈的手机响起,是一条短信
|
||||||
|
△短信内容:"程澈,这只是开始。- 神秘人"
|
||||||
|
|
||||||
|
△程澈看到短信,眉头紧皱
|
||||||
|
△但他没有吵醒云想,而是将她抱得更紧
|
||||||
|
|
||||||
|
程澈(OS):不管还有什么威胁,我都会保护好云想
|
||||||
|
|
||||||
|
【第9集完】
|
||||||
|
|
||||||
|
==== 创作说明 ====
|
||||||
|
|
||||||
|
【第7-9集改编亮点】
|
||||||
|
1. **危机升级**:从情感误会到生命威胁,层层递进
|
||||||
|
2. **人物考验**:通过危机考验两人的真爱
|
||||||
|
3. **反派立体化**:陈总和林雅的阴谋更加复杂
|
||||||
|
4. **情感深化**:通过分离和重聚,感情更加深厚
|
||||||
|
5. **动作戏份**:增加营救戏码,提升观赏性
|
||||||
|
|
||||||
|
【情绪设计】
|
||||||
|
1. **虐心情节**:误会、分离、威胁制造情绪波动
|
||||||
|
2. **甜蜜反转**:危机后的重聚更加珍贵
|
||||||
|
3. **英雄救美**:程澈为爱下跪,展现真爱力量
|
||||||
|
4. **成长弧线**:云想学会为爱勇敢,程澈学会为爱牺牲
|
||||||
|
|
||||||
|
【商业价值】
|
||||||
|
1. **话题性**:霸总为爱下跪、生死营救等话题
|
||||||
|
2. **情绪商品**:虐恋情深,观众情绪投入度高
|
||||||
|
3. **付费点**:营救高潮、真爱告白等关键情节
|
||||||
|
4. **完播率**:每集结尾的悬念设置
|
||||||
|
|
||||||
|
【与原著对比】
|
||||||
|
- 大幅增加动作戏和悬疑元素
|
||||||
|
- 强化反派威胁,提升戏剧张力
|
||||||
|
- 压缩时间线,删除冗余支线
|
||||||
|
- 情感线更加极致化和戏剧化
|
||||||
75
doc/参考/诊断与资产评估.txt
Normal file
75
doc/参考/诊断与资产评估.txt
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
【诊断与资产评估报告】
|
||||||
|
|
||||||
|
==== 故事内核诊断 ====
|
||||||
|
|
||||||
|
核心故事线:校园青春恋爱 + 成长治愈
|
||||||
|
- 失去父亲的孤独少女云想 VS 外冷内热的学霸程澈
|
||||||
|
- 从互相看不顺眼到暗生情愫的经典"欢喜冤家"模式
|
||||||
|
- 治愈系成长:程澈的出现如"一束光"照亮云想的黑暗世界
|
||||||
|
|
||||||
|
情感内核强度:★★★☆☆
|
||||||
|
- 有一定的情感基础,但缺乏足够的戏剧张力
|
||||||
|
- 节奏偏慢,爽点密度不足
|
||||||
|
|
||||||
|
==== 可继承的宝贵资产 ====
|
||||||
|
|
||||||
|
【高光情节】
|
||||||
|
1. 程澈为云想打架护短(第6-7集)- 经典"护妻打脸"素材
|
||||||
|
2. 咖啡店"上帝"调戏桥段(第20集)- 甜宠互动,网感十足
|
||||||
|
3. 音乐会深情献唱(第25集)- 公开示爱,情感高潮
|
||||||
|
4. 程澈霸道赶走搭讪男(第21集)- 占有欲爆棚,观众爽点
|
||||||
|
|
||||||
|
【神来之笔对白】
|
||||||
|
- "她是我的人,想欺负她,先看看自己几斤几两!"
|
||||||
|
- "你是不是舍不得我?" "没有。" "真的?"
|
||||||
|
- "上帝,对不起,请原谅我!"(当众羞辱反转)
|
||||||
|
|
||||||
|
【独特人设闪光点】
|
||||||
|
- 程澈:外冷内热学霸 + 隐藏宠妻属性 + 毒舌傲娇
|
||||||
|
- 云想:治愈系少女 + 天然呆萌 + 坚强独立
|
||||||
|
|
||||||
|
==== 核心问题诊断 ====
|
||||||
|
|
||||||
|
【致命伤】
|
||||||
|
1. 节奏拖沓:27集才到暧昧期,短剧黄金法则是3集定生死
|
||||||
|
2. 爽点稀薄:缺乏密集的"打脸""逆袭""宠溺"等核心爽点
|
||||||
|
3. 冲突不够:程澈家庭接纳云想过于顺利,缺乏戏剧张力
|
||||||
|
4. 人设模糊:两人都太"正常",缺乏极致化标签
|
||||||
|
|
||||||
|
【次要问题】
|
||||||
|
1. 配角功能性不强:宋谨、观鹤等缺乏明确的推动剧情作用
|
||||||
|
2. 情感递进过于平缓:缺乏情绪过山车体验
|
||||||
|
3. 网络梗语不足:对话偏文艺,缺乏当下网感
|
||||||
|
|
||||||
|
==== 短剧改编潜力评估 ====
|
||||||
|
|
||||||
|
商业价值:★★★★☆
|
||||||
|
- 校园恋爱+治愈系是短剧热门赛道
|
||||||
|
- 男女主人设有改造空间,可塑性强
|
||||||
|
- 核心情感线清晰,适合短剧快节奏
|
||||||
|
|
||||||
|
改编难度:★★★☆☆
|
||||||
|
- 需要大幅压缩节奏,前置爽点
|
||||||
|
- 人设需要极致化改造
|
||||||
|
- 冲突线需要重新设计
|
||||||
|
|
||||||
|
==== 初步改编建议 ====
|
||||||
|
|
||||||
|
【核心策略】
|
||||||
|
将温吞校园恋爱魔改为"战神学霸归来,护妻打脸"的极致爽文模式
|
||||||
|
|
||||||
|
【关键改造点】
|
||||||
|
1. 人设极致化:程澈→隐藏身份的天才学霸+宠妻狂魔;云想→被欺凌的灰姑娘+逆袭女王
|
||||||
|
2. 冲突前置:开篇即安排云想被校园霸凌,程澈强势护短
|
||||||
|
3. 爽点密集化:每集至少2个打脸/宠溺/逆袭爽点
|
||||||
|
4. 节奏压缩:27集压缩至12-15集,3集内确立CP关系
|
||||||
|
|
||||||
|
【预期效果】
|
||||||
|
完播率提升40%,付费转化率提升60%
|
||||||
|
|
||||||
|
==== 结论 ====
|
||||||
|
|
||||||
|
这是一个具有良好改编基础的校园恋爱故事,核心情感线和人物关系清晰,但需要进行"爽文化"改造以适应短剧市场。通过节奏压缩、人设极致化、冲突强化等手段,有望打造成爆款短剧。
|
||||||
|
|
||||||
|
改编信心指数:85%
|
||||||
|
预期市场表现:A级(头部20%)
|
||||||
235
doc/参考/重大事件时间线.txt
Normal file
235
doc/参考/重大事件时间线.txt
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
【重大事件时间线.txt - 剧本圣经】
|
||||||
|
|
||||||
|
==== 时间线总览 ====
|
||||||
|
|
||||||
|
【故事时间跨度】:高三下学期(约4个月)
|
||||||
|
【叙事节奏】:快节奏,密集事件推进
|
||||||
|
【关键时间节点】:12个重大事件,对应12集内容
|
||||||
|
|
||||||
|
==== 详细时间线 ====
|
||||||
|
|
||||||
|
【第1周 - 命运相遇】
|
||||||
|
|
||||||
|
**Day 1 - 第1集:霸气出场**
|
||||||
|
- 上午:云想被校园恶霸在食堂羞辱
|
||||||
|
- 中午:程澈霸气出场,一打三护短
|
||||||
|
- 下午:程澈接到神秘电话,身份暗示
|
||||||
|
- 晚上:程澈对云想说"以后跟着我"
|
||||||
|
|
||||||
|
关键转折点:两人命运交汇,保护关系建立
|
||||||
|
情感温度:从0°升至30°
|
||||||
|
|
||||||
|
**Day 2-3 - 第2集:宠妻日常**
|
||||||
|
- Day 2上午:程澈为云想准备爱心早餐
|
||||||
|
- Day 2下午:程澈为云想出头对抗老师
|
||||||
|
- Day 2晚上:程澈送云想回家
|
||||||
|
- Day 3:观鹤温柔出场,成为潜在情敌
|
||||||
|
|
||||||
|
关键转折点:宠妻模式开启,情敌出现
|
||||||
|
情感温度:从30°升至50°
|
||||||
|
|
||||||
|
**Day 4-5 - 第3集:吃醋宣示**
|
||||||
|
- Day 4:观鹤邀请云想参加音乐社
|
||||||
|
- Day 4下午:程澈吃醋,霸道阻止
|
||||||
|
- Day 4晚上:程澈当众宣示主权
|
||||||
|
- Day 5:程澈展现天才实力,神秘人调查
|
||||||
|
|
||||||
|
关键转折点:占有欲爆发,身份线推进
|
||||||
|
情感温度:从50°升至70°
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【第2周 - 身份震撼】
|
||||||
|
|
||||||
|
**Day 6-7 - 第4集:天才曝光**
|
||||||
|
- Day 6:程澈国际奥数冠军身份曝光
|
||||||
|
- Day 6下午:全校震惊,云想自卑
|
||||||
|
- Day 7:云想开始逃避程澈
|
||||||
|
- Day 7晚上:程澈开启追妻模式
|
||||||
|
|
||||||
|
关键转折点:身份差距显现,感情危机
|
||||||
|
情感温度:从70°降至40°
|
||||||
|
|
||||||
|
**Day 8-10 - 第5集:确立关系**
|
||||||
|
- Day 8:云想主动为程澈准备惊喜
|
||||||
|
- Day 9:两人正式确立恋爱关系
|
||||||
|
- Day 10:甜蜜日常,程澈为云想庆生
|
||||||
|
- Day 10晚上:林雅(未婚妻)出现预告
|
||||||
|
|
||||||
|
关键转折点:正式成为情侣,新威胁来临
|
||||||
|
情感温度:从40°飙升至90°
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【第3周 - 情敌考验】
|
||||||
|
|
||||||
|
**Day 11-12 - 第6集:情敌来袭**
|
||||||
|
- Day 11:林雅强势登场,威胁云想
|
||||||
|
- Day 11下午:云想受打击,选择离开
|
||||||
|
- Day 12:程澈愤怒反击,拒绝未婚妻
|
||||||
|
- Day 12晚上:程澈坚定选择云想
|
||||||
|
|
||||||
|
关键转折点:面临情敌威胁,程澈做出选择
|
||||||
|
情感温度:从90°降至60°,再升至85°
|
||||||
|
|
||||||
|
**Day 13-14 - 第7集:家族压力**
|
||||||
|
- Day 13:程澈父亲施压,要求分手
|
||||||
|
- Day 13下午:程澈拒绝,宁可断绝关系
|
||||||
|
- Day 14:云想觉醒,决定为爱而战
|
||||||
|
- Day 14晚上:两人更加珍惜彼此
|
||||||
|
|
||||||
|
关键转折点:家族对抗,云想成长觉醒
|
||||||
|
情感温度:从85°降至50°,再升至80°
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【第4周 - 华丽逆袭】
|
||||||
|
|
||||||
|
**Day 15-16 - 第8集:天赋觉醒**
|
||||||
|
- Day 15:学校音乐比赛报名
|
||||||
|
- Day 16:云想音乐天赋爆发,一鸣惊人
|
||||||
|
- Day 16下午:全校刮目相看
|
||||||
|
- Day 16晚上:程澈为云想骄傲庆祝
|
||||||
|
|
||||||
|
关键转折点:云想华丽逆袭,证明自己
|
||||||
|
情感温度:从80°升至95°
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【第6周 - 生死考验】
|
||||||
|
|
||||||
|
**Day 17-18 - 第9集:最大危机**
|
||||||
|
- Day 17:程澈完整身份曝光(企业继承人+天才黑客)
|
||||||
|
- Day 17下午:商业对手绑架云想
|
||||||
|
- Day 18:程澈动用所有资源营救
|
||||||
|
- Day 18晚上:成功救出云想,消除威胁
|
||||||
|
|
||||||
|
关键转折点:生死考验,感情升华
|
||||||
|
情感温度:从95°降至20°,再飙升至100°
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【第8周 - 公开宣示】
|
||||||
|
|
||||||
|
**Day 19-20 - 第10集:公开关系**
|
||||||
|
- Day 19:学校大会,程澈公开表白
|
||||||
|
- Day 19下午:程澈父母正式接纳云想
|
||||||
|
- Day 20:云想完美蜕变,成为风云人物
|
||||||
|
- Day 20晚上:两人开始规划未来
|
||||||
|
|
||||||
|
关键转折点:公开关系,获得认可
|
||||||
|
情感温度:稳定在100°
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【第10-12周 - 甜蜜日常】
|
||||||
|
|
||||||
|
**Day 21-25 - 第11集:幸福满溢**
|
||||||
|
- Day 21-23:校园甜蜜日常,撒糖不断
|
||||||
|
- Day 24:两人规划大学和未来
|
||||||
|
- Day 25:解决所有遗留问题,达成和解
|
||||||
|
|
||||||
|
关键转折点:进入稳定期,准备结局
|
||||||
|
情感温度:持续100°
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
【第16周 - 完美结局】
|
||||||
|
|
||||||
|
**毕业典礼日 - 第12集:永远幸福**
|
||||||
|
- 上午:毕业典礼,双双获得荣誉
|
||||||
|
- 中午:程澈在典礼上浪漫求婚
|
||||||
|
- 下午:云想含泪答应,全场祝福
|
||||||
|
- 未来:携手走向美好人生
|
||||||
|
|
||||||
|
关键转折点:完美结局,永远幸福
|
||||||
|
情感温度:达到巅峰120°
|
||||||
|
|
||||||
|
==== 关键节点分析 ====
|
||||||
|
|
||||||
|
【情感升温节点】
|
||||||
|
1. Day 1:初次相遇,保护关系建立(0°→30°)
|
||||||
|
2. Day 2-3:宠妻日常开启(30°→50°)
|
||||||
|
3. Day 4-5:占有欲爆发(50°→70°)
|
||||||
|
4. Day 9:确立恋爱关系(40°→90°)
|
||||||
|
5. Day 16:云想逆袭成功(80°→95°)
|
||||||
|
6. Day 18:生死考验后(20°→100°)
|
||||||
|
|
||||||
|
【情感危机节点】
|
||||||
|
1. Day 6:身份差距显现(70°→40°)
|
||||||
|
2. Day 11:情敌威胁(90°→60°)
|
||||||
|
3. Day 13:家族压力(85°→50°)
|
||||||
|
4. Day 17:生死危机(95°→20°)
|
||||||
|
|
||||||
|
【重大转折节点】
|
||||||
|
1. Day 1:命运相遇
|
||||||
|
2. Day 6:身份曝光
|
||||||
|
3. Day 9:确立关系
|
||||||
|
4. Day 11:情敌出现
|
||||||
|
5. Day 16:华丽逆袭
|
||||||
|
6. Day 17:生死考验
|
||||||
|
7. Day 19:公开宣示
|
||||||
|
8. 毕业日:完美结局
|
||||||
|
|
||||||
|
==== 节奏控制策略 ====
|
||||||
|
|
||||||
|
【快节奏推进原则】
|
||||||
|
- 每2-3天一个重大事件
|
||||||
|
- 情感温度大起大落,制造过山车体验
|
||||||
|
- 危机与甜蜜交替出现,保持观众情绪在线
|
||||||
|
|
||||||
|
【爽点分布密度】
|
||||||
|
- 护短打脸:Day 1, 4, 11, 17
|
||||||
|
- 宠妻甜蜜:Day 2, 9, 16, 21-25
|
||||||
|
- 身份震撼:Day 1, 6, 17
|
||||||
|
- 逆袭反击:Day 6, 16, 19
|
||||||
|
- 占有宣示:Day 4, 12, 19
|
||||||
|
|
||||||
|
【钩子设置时机】
|
||||||
|
每个重大事件结束时都设置强钩子:
|
||||||
|
- Day 1结尾:"以后跟着我,没人敢欺负你"
|
||||||
|
- Day 3结尾:神秘人要揭露程澈身份
|
||||||
|
- Day 5结尾:程澈身份即将曝光
|
||||||
|
- Day 7结尾:云想决定努力变优秀
|
||||||
|
- Day 10结尾:程澈未婚妻出现
|
||||||
|
- Day 12结尾:家族要断绝父子关系
|
||||||
|
- Day 14结尾:云想隐藏天赋被发现
|
||||||
|
- Day 16结尾:更大危机即将来临
|
||||||
|
- Day 18结尾:两人感情更加坚定
|
||||||
|
- Day 20结尾:开始规划美好未来
|
||||||
|
- Day 25结尾:毕业典礼即将到来
|
||||||
|
|
||||||
|
==== 商业化时间节点 ====
|
||||||
|
|
||||||
|
【付费转化关键时刻】
|
||||||
|
1. Day 4晚上:程澈吃醋大戏(第3集结尾)
|
||||||
|
2. Day 11下午:情敌对决高潮(第6集结尾)
|
||||||
|
3. Day 17晚上:生死营救(第9集结尾)
|
||||||
|
4. 毕业典礼:浪漫求婚(第12集结尾)
|
||||||
|
|
||||||
|
【完播率保障节点】
|
||||||
|
每集结尾的强钩子确保观众必须看下一集
|
||||||
|
|
||||||
|
==== 原著对比时间线 ====
|
||||||
|
|
||||||
|
【原著27集 vs 改编12集】
|
||||||
|
- 原著1-9集内容 → 改编第1-2集(大幅压缩日常,突出冲突)
|
||||||
|
- 原著10-18集内容 → 改编第3-6集(强化情敌线,增加家族压力)
|
||||||
|
- 原著19-27集内容 → 改编第7-12集(新增生死考验,强化逆袭)
|
||||||
|
|
||||||
|
【关键改动】
|
||||||
|
1. 删除大量日常铺垫,直接进入冲突
|
||||||
|
2. 前置音乐会情节,作为逆袭高光时刻
|
||||||
|
3. 新增绑架营救情节,增强戏剧张力
|
||||||
|
4. 压缩感情发展时间,快速确立关系
|
||||||
|
5. 强化反派戏份,增加打脸爽点
|
||||||
|
|
||||||
|
==== 执行要点 ====
|
||||||
|
|
||||||
|
1. **严格按时间线执行**:确保每个关键节点按时到达
|
||||||
|
2. **情感温度监控**:实时监控观众情绪,适时调整
|
||||||
|
3. **钩子强度检验**:每个钩子都要能让观众"停不下来"
|
||||||
|
4. **节奏感把控**:快慢结合,张弛有度
|
||||||
|
5. **商业节点重点**:付费转化时刻要重点打磨
|
||||||
|
|
||||||
|
这份时间线将确保我们的改编节奏紧凑,情节密集,每个关键节点都能最大化观众的情绪体验和商业价值。
|
||||||
0
graph/__init__.py
Normal file
0
graph/__init__.py
Normal file
292
graph/test_agent_graph_1.py
Normal file
292
graph/test_agent_graph_1.py
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
"""智能编剧系统工作流图定义
|
||||||
|
|
||||||
|
该模块定义了智能编剧系统的完整工作流程图,包括各个节点和边的连接关系。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import TypedDict, Annotated, Dict, Any, List, TypedDict, Optional
|
||||||
|
from agent.scheduler import SchedulerAgent
|
||||||
|
from agent.build_bible import BuildBibleAgent
|
||||||
|
from agent.episode_create import EpisodeCreateAgent
|
||||||
|
from agent.script_analysis import ScriptAnalysisAgent
|
||||||
|
from agent.strategic_planning import StrategicPlanningAgent
|
||||||
|
|
||||||
|
from langgraph.graph import StateGraph, START, END
|
||||||
|
from utils.logger import get_logger
|
||||||
|
import operator
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
# 定义一个简单的替换函数
|
||||||
|
def replace_value(old_val, new_val):
|
||||||
|
"""一个简单的合并函数,用于替换旧值"""
|
||||||
|
return new_val
|
||||||
|
|
||||||
|
# 状态类型定义
|
||||||
|
class InputState(TypedDict):
|
||||||
|
"""工作流输入状态"""
|
||||||
|
input_data: Annotated[Dict[str, Any], operator.add]
|
||||||
|
session_id: Annotated[str, replace_value]
|
||||||
|
|
||||||
|
class OutputState(TypedDict):
|
||||||
|
"""工作流输出状态"""
|
||||||
|
session_id: Annotated[str, replace_value]
|
||||||
|
status: Annotated[str, replace_value]
|
||||||
|
error: Annotated[str, replace_value]
|
||||||
|
|
||||||
|
class NodeInfo(TypedDict):
|
||||||
|
"""工作流信息"""
|
||||||
|
step: Annotated[str, replace_value] # 阶段名称 [wait_for_input,script_analysis,strategic_planning,build_bible,episode_create_loop, finish]
|
||||||
|
status: Annotated[str, replace_value] # 当前阶段的状态 [waiting,running,failed,completed]
|
||||||
|
reason: Annotated[str, replace_value] # 失败原因
|
||||||
|
retry_count: Annotated[int, replace_value] # 重试次数
|
||||||
|
from_type: Annotated[str, replace_value] # 本次请求来着哪里 [user, agent]
|
||||||
|
|
||||||
|
|
||||||
|
class ScriptwriterState(TypedDict, total=False):
|
||||||
|
"""智能编剧工作流整体状态"""
|
||||||
|
# 输入数据
|
||||||
|
input_data: Annotated[Dict[str, Any], operator.add]
|
||||||
|
session_id: Annotated[str, replace_value]
|
||||||
|
|
||||||
|
# 节点间状态
|
||||||
|
node_info: NodeInfo
|
||||||
|
|
||||||
|
# 中间状态
|
||||||
|
agent_script_id: Annotated[str, replace_value] # 剧本ID 包括原文
|
||||||
|
agent_plan: Annotated[Dict[str, Any], replace_value] #剧本计划
|
||||||
|
script_bible: Annotated[Dict[str, Any], replace_value] #剧本圣经
|
||||||
|
episode_list: Annotated[List, replace_value] # 章节列表 完成状态、产出章节id
|
||||||
|
|
||||||
|
# 输出数据
|
||||||
|
status: Annotated[str, replace_value]
|
||||||
|
error: Annotated[str, replace_value]
|
||||||
|
|
||||||
|
class ScriptwriterGraph:
|
||||||
|
"""智能编剧工作流图类
|
||||||
|
|
||||||
|
管理智能编剧系统的完整工作流程,包括:
|
||||||
|
- 剧本接收
|
||||||
|
- 诊断分析
|
||||||
|
- 策略制定
|
||||||
|
- 剧本圣经构建
|
||||||
|
- 剧本创作
|
||||||
|
- 迭代调整
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""初始化工作流图"""
|
||||||
|
self.graph = None
|
||||||
|
self._build_graph()
|
||||||
|
|
||||||
|
|
||||||
|
def node_router(self, state: ScriptwriterState) -> str:
|
||||||
|
next_node = state.get("node", '')
|
||||||
|
if next_node:
|
||||||
|
return next_node
|
||||||
|
else:
|
||||||
|
return END
|
||||||
|
|
||||||
|
def _build_graph(self) -> None:
|
||||||
|
"""构建工作流图"""
|
||||||
|
try:
|
||||||
|
# 创建智能体
|
||||||
|
# 调度智能体
|
||||||
|
schedulerAgent = SchedulerAgent(
|
||||||
|
tools=[],
|
||||||
|
SchedulerList=[
|
||||||
|
{
|
||||||
|
"scheduler_node": "调度智能体节点",
|
||||||
|
"script_analysis_node": "原始剧本分析节点",
|
||||||
|
"strategic_planning_node": "确立改编目标节点",
|
||||||
|
"build_bible_node": "剧本圣经构建节点",
|
||||||
|
"episode_create_node": "单集创作节点",
|
||||||
|
"end_node": "结束节点,任务失败终止时使用,结束后整个工作流将停止"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
scriptAnalysisAgent = ScriptAnalysisAgent(
|
||||||
|
tools=[],
|
||||||
|
SchedulerList=[
|
||||||
|
{
|
||||||
|
"scheduler_node": "调度智能体节点",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
strategicPlanningAgent = StrategicPlanningAgent(
|
||||||
|
tools=[],
|
||||||
|
SchedulerList=[
|
||||||
|
{
|
||||||
|
"scheduler_node": "调度智能体节点",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
buildBibleAgent = BuildBibleAgent(
|
||||||
|
tools=[],
|
||||||
|
SchedulerList=[
|
||||||
|
{
|
||||||
|
"scheduler_node": "调度智能体节点",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
episodeCreate = EpisodeCreateAgent(
|
||||||
|
tools=[],
|
||||||
|
SchedulerList=[
|
||||||
|
{
|
||||||
|
"scheduler_node": "调度智能体节点",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建状态图
|
||||||
|
workflow = StateGraph(ScriptwriterState, input_schema=InputState, output_schema=OutputState)
|
||||||
|
|
||||||
|
# 添加节点
|
||||||
|
workflow.add_node("scheduler_node", self.scheduler_node)
|
||||||
|
workflow.add_node("script_analysis_node", self.script_analysis_node)
|
||||||
|
workflow.add_node("strategic_planning_node", self.strategic_planning_node)
|
||||||
|
workflow.add_node("build_bible_node", self.build_bible_node)
|
||||||
|
workflow.add_node("episode_create_node", self.episode_create_node)
|
||||||
|
workflow.add_node("end_node", self.end_node)
|
||||||
|
|
||||||
|
# 添加边
|
||||||
|
workflow.set_entry_point("scheduler_node")
|
||||||
|
# 所有功能节点执行完成后,都返回给调度节点
|
||||||
|
workflow.add_edge("script_analysis_node", "scheduler_node")
|
||||||
|
workflow.add_edge("strategic_planning_node", "scheduler_node")
|
||||||
|
workflow.add_edge("build_bible_node", "scheduler_node")
|
||||||
|
workflow.add_edge("episode_create_node", "scheduler_node")
|
||||||
|
|
||||||
|
# 添加条件边:由调度节点决定下一个路由
|
||||||
|
workflow.add_conditional_edges(
|
||||||
|
"scheduler_node",
|
||||||
|
self.node_router,
|
||||||
|
{
|
||||||
|
"script_analysis_node": "script_analysis_node",
|
||||||
|
"strategic_planning_node": "strategic_planning_node",
|
||||||
|
"build_bible_node": "build_bible_node",
|
||||||
|
"episode_create_node": "episode_create_node",
|
||||||
|
# 用户确认和暂停逻辑在这里处理,不需要单独的边
|
||||||
|
"end_node": "end_node",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
workflow.add_edge("end_node", END)
|
||||||
|
|
||||||
|
# 编译图
|
||||||
|
self.graph = workflow.compile()
|
||||||
|
logger.info("工作流图构建完成")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"构建工作流图失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# --- 定义图中的节点 ---
|
||||||
|
async def scheduler_node(self, state: ScriptwriterState)-> ScriptwriterState:
|
||||||
|
"""第一步:初步沟通,请求剧本"""
|
||||||
|
session_id = state.get("session_id", "")
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
async def script_analysis_node(self, state: ScriptwriterState)-> ScriptwriterState:
|
||||||
|
"""第二步:诊断分析与资产评估"""
|
||||||
|
print("\n--- 正在进行诊断分析 ---")
|
||||||
|
session_id = state.get("session_id", "")
|
||||||
|
print(f"报告已生成: TEST")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
async def confirm_analysis_node(self, state: ScriptwriterState)-> ScriptwriterState:
|
||||||
|
"""用户确认分析报告节点"""
|
||||||
|
print("\n等待用户确认分析报告...")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
async def strategic_planning_node(self, state: ScriptwriterState)-> ScriptwriterState:
|
||||||
|
"""第三步:确立改编目标与战略蓝图"""
|
||||||
|
print("\n--- 正在制定战略蓝图 ---")
|
||||||
|
print(f"战略蓝图已生成: TEST")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
async def build_bible_node(self, state: ScriptwriterState)-> ScriptwriterState:
|
||||||
|
"""第四步:确立改编目标与战略蓝图"""
|
||||||
|
print("\n--- 正在制定战略蓝图 ---")
|
||||||
|
print(f"战略蓝图已生成: TEST")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
async def episode_create_node(self, state: ScriptwriterState)-> ScriptwriterState:
|
||||||
|
"""第五步:动态创作与闭环校验(循环主体)"""
|
||||||
|
num_episodes = 3 # 假设每次创作3集
|
||||||
|
episode_list = []
|
||||||
|
return {"episode_list": episode_list}
|
||||||
|
|
||||||
|
async def end_node(self, state: ScriptwriterState)-> OutputState:
|
||||||
|
""" 结束节点 处理并完成所有数据状态 """
|
||||||
|
print(f"langgraph 所有任务完成")
|
||||||
|
return {
|
||||||
|
"session_id": state.get("session_id", ""),
|
||||||
|
"status": "",
|
||||||
|
"error": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
async def run(self, input_data: Dict[str, Any]) -> OutputState:
|
||||||
|
"""运行工作流
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input_data: 输入数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
工作流执行结果
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.info("开始运行智能编剧工作流")
|
||||||
|
|
||||||
|
# # 初始化状态
|
||||||
|
# initial_state: InputState = {
|
||||||
|
# 'input_data': input_data,
|
||||||
|
# 'session_id': input_data.get('session_id', ''),
|
||||||
|
# 'max_iterations': input_data.get('max_iterations', 3),
|
||||||
|
# 'batch_info': input_data.get('batch_info', {})
|
||||||
|
# }
|
||||||
|
|
||||||
|
# # 运行工作流
|
||||||
|
# if self.graph is None:
|
||||||
|
# raise RuntimeError("工作流图未正确初始化")
|
||||||
|
|
||||||
|
# result = await self.graph.ainvoke(initial_state)
|
||||||
|
# logger.info(f"工作流执行结果: {result}")
|
||||||
|
# if not result:
|
||||||
|
# raise ValueError("工作流执行结果为空")
|
||||||
|
# # 保存到记忆
|
||||||
|
# self.memory.save_workflow_result(result)
|
||||||
|
|
||||||
|
# # 构造输出状态
|
||||||
|
# output_result: OutputState = {
|
||||||
|
# 'script': result.get('script'),
|
||||||
|
# 'adjustment': result.get('adjustment'),
|
||||||
|
# 'error': result.get('error'),
|
||||||
|
# 'iteration_count': result.get('iteration_count', 0)
|
||||||
|
# }
|
||||||
|
output_result:OutputState = {
|
||||||
|
'session_id': "",
|
||||||
|
'status': 'completed',
|
||||||
|
'error': '',
|
||||||
|
}
|
||||||
|
logger.info("智能编剧工作流运行完成")
|
||||||
|
return output_result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"运行工作流失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def get_graph_visualization(self) -> str:
|
||||||
|
"""获取工作流图的可视化表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
图的文本表示
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if self.graph:
|
||||||
|
return str(self.graph)
|
||||||
|
return "工作流图未初始化"
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取图可视化失败: {e}")
|
||||||
|
return f"获取图可视化失败: {e}"
|
||||||
171
graph/test_graph_3.py
Normal file
171
graph/test_graph_3.py
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
# test_graph_persistent.py
|
||||||
|
from operator import add
|
||||||
|
from typing import TypedDict, Annotated
|
||||||
|
from langchain_core.messages import AnyMessage, HumanMessage
|
||||||
|
from langgraph import graph
|
||||||
|
from langgraph.config import get_stream_writer
|
||||||
|
from langgraph.constants import END, START
|
||||||
|
from langgraph.graph import StateGraph
|
||||||
|
from IPython.display import Image
|
||||||
|
import uuid
|
||||||
|
import config
|
||||||
|
# 导入数据库连接和自定义检查点存储器
|
||||||
|
from tools.database.mongo import mainDB, client
|
||||||
|
# from mongodb_checkpointer import MongoDBCheckpointSaver
|
||||||
|
from langgraph.checkpoint.mongodb import MongoDBSaver
|
||||||
|
import asyncio
|
||||||
|
# collection = db.langgraph_checkpoints
|
||||||
|
memory: MongoDBSaver = MongoDBSaver(client, db_name=config.MONGO_CHECKPOINT_DB_NAME)
|
||||||
|
|
||||||
|
class State(TypedDict):
|
||||||
|
messages: Annotated[list[AnyMessage], add]
|
||||||
|
type: str
|
||||||
|
|
||||||
|
class InputState(TypedDict):
|
||||||
|
user_input: str
|
||||||
|
|
||||||
|
class OutputState(TypedDict):
|
||||||
|
graph_output: str
|
||||||
|
|
||||||
|
class OverallState(TypedDict):
|
||||||
|
foo: str
|
||||||
|
user_input: str
|
||||||
|
graph_output: str
|
||||||
|
|
||||||
|
class PrivateState(TypedDict):
|
||||||
|
bar: str
|
||||||
|
|
||||||
|
async def node_1(state: InputState) -> OverallState:
|
||||||
|
print(f"Node 1 处理: {state['user_input']}")
|
||||||
|
return {"foo": state["user_input"] + ">学院"}
|
||||||
|
|
||||||
|
async def node_2(state: OverallState) -> PrivateState:
|
||||||
|
print(f"Node 2 处理: {state['foo']}")
|
||||||
|
return {"bar": state["foo"] + ">非常"}
|
||||||
|
|
||||||
|
async def node_3(state: PrivateState) -> OverallState:
|
||||||
|
print(f"Node 3 处理: {state['bar']}")
|
||||||
|
return {"graph_output": state["bar"] + ">靠谱"}
|
||||||
|
|
||||||
|
# 创建 MongoDB 检查点存储器
|
||||||
|
# checkpointer = MongoDBCheckpointSaver(db.langgraph_checkpoints)
|
||||||
|
|
||||||
|
# 构建图,并添加检查点存储器
|
||||||
|
builder = StateGraph(OverallState, input_schema=InputState, output_schema=OutputState)
|
||||||
|
|
||||||
|
builder.add_node('node_1', node_1)
|
||||||
|
builder.add_node('node_2', node_2)
|
||||||
|
builder.add_node('node_3', node_3)
|
||||||
|
|
||||||
|
builder.add_edge(START, 'node_1')
|
||||||
|
builder.add_edge('node_1', 'node_2')
|
||||||
|
builder.add_edge('node_2', 'node_3')
|
||||||
|
builder.add_edge('node_3', END)
|
||||||
|
|
||||||
|
# 编译图并添加检查点存储器
|
||||||
|
graph = builder.compile(checkpointer=memory)
|
||||||
|
|
||||||
|
async def run_with_persistence(user_input: str, thread_id: str = None):
|
||||||
|
"""运行带持久化的图"""
|
||||||
|
if thread_id is None:
|
||||||
|
thread_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
print(f"使用线程 ID: {thread_id}")
|
||||||
|
|
||||||
|
# 配置包含线程 ID
|
||||||
|
config = {"configurable": {"thread_id": thread_id}}
|
||||||
|
|
||||||
|
# 执行图
|
||||||
|
input_state = {"user_input": user_input}
|
||||||
|
output_state = await graph.ainvoke(input_state, config)
|
||||||
|
|
||||||
|
print(f"输出: {output_state}")
|
||||||
|
return output_state, thread_id
|
||||||
|
|
||||||
|
async def get_checkpoint_history(thread_id: str):
|
||||||
|
"""获取检查点历史"""
|
||||||
|
config = {"configurable": {"thread_id": thread_id}}
|
||||||
|
|
||||||
|
try:
|
||||||
|
history_generator = memory.list(config, limit=10)
|
||||||
|
|
||||||
|
print("正在获取检查点历史...")
|
||||||
|
|
||||||
|
# 使用列表推导式或 for 循环来收集所有检查点
|
||||||
|
history = list(history_generator)
|
||||||
|
|
||||||
|
print(f"找到 {len(history)} 个检查点:")
|
||||||
|
|
||||||
|
for i, checkpoint_tuple in enumerate(history):
|
||||||
|
# checkpoint_tuple 包含 config, checkpoint, metadata 等属性
|
||||||
|
# print(f" - ID: {checkpoint_tuple}")
|
||||||
|
checkpoint_data = checkpoint_tuple.checkpoint
|
||||||
|
metadata = checkpoint_tuple.metadata
|
||||||
|
print(f"检查点 {i+1}:")
|
||||||
|
print(f" - ID: {checkpoint_data.get('id', 'N/A')}")
|
||||||
|
print(f" - 状态: {checkpoint_data.get('channel_values', {})}")
|
||||||
|
print(f" - 元数据: {metadata}")
|
||||||
|
print("-" * 50)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"获取历史记录时出错: {e}")
|
||||||
|
|
||||||
|
def resume_from_checkpoint(thread_id: str, checkpoint_id: str = None):
|
||||||
|
"""从检查点恢复执行"""
|
||||||
|
config = {"configurable": {"thread_id": thread_id}}
|
||||||
|
|
||||||
|
if checkpoint_id:
|
||||||
|
config["configurable"]["checkpoint_id"] = checkpoint_id
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 获取 CheckpointTuple 对象
|
||||||
|
checkpoint_tuple = memory.get_tuple(config)
|
||||||
|
|
||||||
|
if checkpoint_tuple:
|
||||||
|
# 直接通过属性访问,而不是解包
|
||||||
|
checkpoint_data = checkpoint_tuple.checkpoint
|
||||||
|
metadata = checkpoint_tuple.metadata
|
||||||
|
print(f"从检查点恢复:")
|
||||||
|
print(f" - 检查点 ID: {checkpoint_data.get('id', 'N/A')}")
|
||||||
|
print(f" - 状态: {checkpoint_data.get('channel_values', {})}")
|
||||||
|
print(f" - 元数据: {metadata}")
|
||||||
|
return checkpoint_data.get('channel_values', {})
|
||||||
|
else:
|
||||||
|
print(f"未找到线程 {thread_id} 的检查点")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"恢复检查点时出错: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("=== 测试持久化 LangGraph ===")
|
||||||
|
|
||||||
|
# 第一次运行
|
||||||
|
print("\n1. 第一次运行:")
|
||||||
|
# 由于在异步函数外使用await,需要使用asyncio运行
|
||||||
|
output1, thread_id = asyncio.run(run_with_persistence("你好"))
|
||||||
|
|
||||||
|
# 查看检查点历史
|
||||||
|
print("\n2. 查看检查点历史:")
|
||||||
|
asyncio.run(get_checkpoint_history(thread_id))
|
||||||
|
|
||||||
|
# 从同一线程继续运行
|
||||||
|
print("\n3. 从同一线程继续运行:")
|
||||||
|
output2, _ = asyncio.run(run_with_persistence("再见", thread_id))
|
||||||
|
|
||||||
|
# 查看更新后的历史
|
||||||
|
print("\n4. 查看更新后的历史:")
|
||||||
|
asyncio.run(get_checkpoint_history(thread_id))
|
||||||
|
|
||||||
|
# 恢复检查点状态
|
||||||
|
print("\n5. 恢复最新检查点状态:")
|
||||||
|
restored_state = resume_from_checkpoint(thread_id)
|
||||||
|
|
||||||
|
# 可视化图结构(可选)
|
||||||
|
# try:
|
||||||
|
# with open('persistent_graph_visualization.png', 'wb') as f:
|
||||||
|
# f.write(graph.get_graph().draw_mermaid_png())
|
||||||
|
# print("\n图片已保存为 persistent_graph_visualization.png")
|
||||||
|
# except Exception as e:
|
||||||
|
# print(f"保存可视化图片失败: {e}")
|
||||||
4
handlers/__init__.py
Normal file
4
handlers/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# handlers/__init__.py
|
||||||
|
"""
|
||||||
|
处理器模块初始化文件
|
||||||
|
"""
|
||||||
84
handlers/langgraph_handler.py
Normal file
84
handlers/langgraph_handler.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
from flask import request, jsonify
|
||||||
|
import asyncio
|
||||||
|
import uuid
|
||||||
|
from graph.test_graph_3 import run_with_persistence, get_checkpoint_history, resume_from_checkpoint
|
||||||
|
|
||||||
|
def run_async(coro):
|
||||||
|
"""运行异步函数的辅助函数"""
|
||||||
|
try:
|
||||||
|
# 尝试使用现有的事件循环
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
# 如果没有运行中的事件循环,则创建一个新的
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
return loop.run_until_complete(coro)
|
||||||
|
|
||||||
|
def run_langgraph():
|
||||||
|
"""启动一个新的langgraph任务"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
user_input = data.get('user_input', '')
|
||||||
|
thread_id = data.get('thread_id', str(uuid.uuid4()))
|
||||||
|
|
||||||
|
if not user_input:
|
||||||
|
return jsonify({'error': 'user_input is required'}), 400
|
||||||
|
|
||||||
|
# 运行异步函数
|
||||||
|
output, thread_id = run_async(run_with_persistence(user_input, thread_id))
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'thread_id': thread_id,
|
||||||
|
'output': output
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
def get_task_status(thread_id):
|
||||||
|
"""查询任务状态和历史"""
|
||||||
|
try:
|
||||||
|
# 获取检查点历史
|
||||||
|
# 注意:这里需要修改get_checkpoint_history以返回数据而不是打印
|
||||||
|
# history = run_async(get_checkpoint_history(thread_id))
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'thread_id': thread_id,
|
||||||
|
'message': 'Task status endpoint'
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
def resume_task(thread_id):
|
||||||
|
"""从检查点恢复任务"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
checkpoint_id = data.get('checkpoint_id')
|
||||||
|
|
||||||
|
# 恢复检查点状态
|
||||||
|
restored_state = resume_from_checkpoint(thread_id, checkpoint_id)
|
||||||
|
|
||||||
|
if restored_state:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'restored_state': restored_state
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
return jsonify({'error': 'Failed to restore checkpoint'}), 404
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
def visualize_graph(thread_id):
|
||||||
|
"""可视化图结构"""
|
||||||
|
try:
|
||||||
|
# 这里可以返回图的可视化信息
|
||||||
|
# 为了简化,我们只返回基本信息
|
||||||
|
return jsonify({
|
||||||
|
'status': 'success',
|
||||||
|
'thread_id': thread_id,
|
||||||
|
'message': 'Graph visualization endpoint'
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
0
models/__init__.py
Normal file
0
models/__init__.py
Normal file
397
models/script_model.py
Normal file
397
models/script_model.py
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
会话数据模型
|
||||||
|
|
||||||
|
定义会话的数据结构和验证逻辑
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, List, Optional, Any
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class SessionStatus(Enum):
|
||||||
|
"""会话状态枚举"""
|
||||||
|
QUEUED = "queued" # 排队中
|
||||||
|
PENDING = "pending" # 等待中
|
||||||
|
ACTIVE = "active" # 活跃状态
|
||||||
|
PAUSED = "paused" # 暂停状态
|
||||||
|
COMPLETED = "completed" # 已完成
|
||||||
|
CANCELLED = "cancelled" # 已取消
|
||||||
|
ERROR = "error" # 错误状态
|
||||||
|
|
||||||
|
class SessionType(Enum):
|
||||||
|
"""会话类型枚举"""
|
||||||
|
SCRIPTWRITING = "scriptwriting" # 编剧创作
|
||||||
|
CONSULTATION = "consultation" # 咨询对话
|
||||||
|
REVIEW = "review" # 剧本评审
|
||||||
|
COLLABORATION = "collaboration" # 协作创作
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SessionModel:
|
||||||
|
"""
|
||||||
|
会话数据模型
|
||||||
|
|
||||||
|
定义会话的完整数据结构
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 基础信息
|
||||||
|
user_id: str
|
||||||
|
session_type: str = SessionType.SCRIPTWRITING.value
|
||||||
|
title: str = ""
|
||||||
|
description: str = ""
|
||||||
|
# 原始剧本内容
|
||||||
|
original_script: str = ""
|
||||||
|
|
||||||
|
# 状态信息
|
||||||
|
status: str = SessionStatus.ACTIVE.value
|
||||||
|
current_step: int = 1
|
||||||
|
total_steps: int = 6
|
||||||
|
|
||||||
|
# 配置信息
|
||||||
|
settings: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
# 时间信息
|
||||||
|
created_at: datetime = field(default_factory=datetime.now)
|
||||||
|
updated_at: datetime = field(default_factory=datetime.now)
|
||||||
|
expires_at: datetime = field(default_factory=lambda: datetime.now() + timedelta(days=7))
|
||||||
|
|
||||||
|
# 可选字段
|
||||||
|
session_id: Optional[str] = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
"""初始化后处理"""
|
||||||
|
# 验证数据
|
||||||
|
self.validate()
|
||||||
|
|
||||||
|
# 设置默认值
|
||||||
|
if not self.title:
|
||||||
|
self.title = f"编剧会话 - {self.created_at.strftime('%Y-%m-%d %H:%M')}"
|
||||||
|
|
||||||
|
# 确保设置字典存在必要的键
|
||||||
|
default_settings = {
|
||||||
|
'auto_save': True,
|
||||||
|
'step_timeout': 1800, # 30分钟
|
||||||
|
'max_retries': 3,
|
||||||
|
'language': 'zh-CN',
|
||||||
|
'genre': 'drama',
|
||||||
|
'style': 'modern'
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value in default_settings.items():
|
||||||
|
if key not in self.settings:
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
# 确保元数据字典存在
|
||||||
|
if 'tags' not in self.metadata:
|
||||||
|
self.metadata['tags'] = []
|
||||||
|
if 'source' not in self.metadata:
|
||||||
|
self.metadata['source'] = 'web_interface'
|
||||||
|
|
||||||
|
def validate(self) -> bool:
|
||||||
|
"""
|
||||||
|
验证数据有效性
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否有效
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: 数据无效时抛出异常
|
||||||
|
"""
|
||||||
|
# 验证用户ID
|
||||||
|
if not self.user_id or not isinstance(self.user_id, str):
|
||||||
|
raise ValueError("用户ID不能为空且必须是字符串")
|
||||||
|
|
||||||
|
# 验证会话类型
|
||||||
|
valid_types = [t.value for t in SessionType]
|
||||||
|
if self.session_type not in valid_types:
|
||||||
|
raise ValueError(f"无效的会话类型: {self.session_type},有效值: {valid_types}")
|
||||||
|
|
||||||
|
# 验证状态
|
||||||
|
valid_statuses = [s.value for s in SessionStatus]
|
||||||
|
if self.status not in valid_statuses:
|
||||||
|
raise ValueError(f"无效的会话状态: {self.status},有效值: {valid_statuses}")
|
||||||
|
|
||||||
|
# 验证步骤
|
||||||
|
if not isinstance(self.current_step, int) or self.current_step < 1:
|
||||||
|
raise ValueError("当前步骤必须是大于0的整数")
|
||||||
|
|
||||||
|
if not isinstance(self.total_steps, int) or self.total_steps < 1:
|
||||||
|
raise ValueError("总步骤数必须是大于0的整数")
|
||||||
|
|
||||||
|
if self.current_step > self.total_steps:
|
||||||
|
raise ValueError("当前步骤不能超过总步骤数")
|
||||||
|
|
||||||
|
# 验证时间
|
||||||
|
if self.updated_at < self.created_at:
|
||||||
|
raise ValueError("更新时间不能早于创建时间")
|
||||||
|
|
||||||
|
if self.expires_at <= self.created_at:
|
||||||
|
raise ValueError("过期时间必须晚于创建时间")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
转换为字典格式
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 字典数据
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'user_id': self.user_id,
|
||||||
|
'session_type': self.session_type,
|
||||||
|
'title': self.title,
|
||||||
|
'description': self.description,
|
||||||
|
'original_script': self.original_script,
|
||||||
|
'status': self.status,
|
||||||
|
'current_step': self.current_step,
|
||||||
|
'total_steps': self.total_steps,
|
||||||
|
'settings': self.settings.copy(),
|
||||||
|
'metadata': self.metadata.copy(),
|
||||||
|
'created_at': self.created_at,
|
||||||
|
'updated_at': self.updated_at,
|
||||||
|
'expires_at': self.expires_at
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.session_id:
|
||||||
|
data['session_id'] = self.session_id
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: Dict[str, Any]) -> 'SessionModel':
|
||||||
|
"""
|
||||||
|
从字典创建模型实例
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: 字典数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SessionModel: 模型实例
|
||||||
|
"""
|
||||||
|
# 提取必需字段
|
||||||
|
user_id = data.get('user_id')
|
||||||
|
if not user_id:
|
||||||
|
raise ValueError("缺少必需字段: user_id")
|
||||||
|
|
||||||
|
# 创建实例
|
||||||
|
instance = cls(
|
||||||
|
user_id=user_id,
|
||||||
|
session_type=data.get('session_type', SessionType.SCRIPTWRITING.value),
|
||||||
|
title=data.get('title', ''),
|
||||||
|
description=data.get('description', ''),
|
||||||
|
status=data.get('status', SessionStatus.ACTIVE.value),
|
||||||
|
current_step=data.get('current_step', 1),
|
||||||
|
total_steps=data.get('total_steps', 6),
|
||||||
|
settings=data.get('settings', {}),
|
||||||
|
metadata=data.get('metadata', {})
|
||||||
|
)
|
||||||
|
|
||||||
|
# 设置时间字段
|
||||||
|
if 'created_at' in data:
|
||||||
|
instance.created_at = data['created_at']
|
||||||
|
if 'updated_at' in data:
|
||||||
|
instance.updated_at = data['updated_at']
|
||||||
|
if 'expires_at' in data:
|
||||||
|
instance.expires_at = data['expires_at']
|
||||||
|
|
||||||
|
# 设置可选字段
|
||||||
|
if 'session_id' in data:
|
||||||
|
instance.session_id = data['session_id']
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def update_status(self, new_status: str) -> None:
|
||||||
|
"""
|
||||||
|
更新会话状态
|
||||||
|
|
||||||
|
Args:
|
||||||
|
new_status: 新状态
|
||||||
|
"""
|
||||||
|
valid_statuses = [s.value for s in SessionStatus]
|
||||||
|
if new_status not in valid_statuses:
|
||||||
|
raise ValueError(f"无效的会话状态: {new_status}")
|
||||||
|
|
||||||
|
self.status = new_status
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def advance_step(self) -> bool:
|
||||||
|
"""
|
||||||
|
推进到下一步
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否成功推进
|
||||||
|
"""
|
||||||
|
if self.current_step < self.total_steps:
|
||||||
|
self.current_step += 1
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
# 如果到达最后一步,标记为完成
|
||||||
|
if self.current_step == self.total_steps:
|
||||||
|
self.status = SessionStatus.COMPLETED.value
|
||||||
|
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def reset_step(self, step_number: int) -> bool:
|
||||||
|
"""
|
||||||
|
重置到指定步骤
|
||||||
|
|
||||||
|
Args:
|
||||||
|
step_number: 目标步骤号
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否成功重置
|
||||||
|
"""
|
||||||
|
if 1 <= step_number <= self.total_steps:
|
||||||
|
self.current_step = step_number
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
# 如果从完成状态回退,更新状态
|
||||||
|
if self.status == SessionStatus.COMPLETED.value and step_number < self.total_steps:
|
||||||
|
self.status = SessionStatus.ACTIVE.value
|
||||||
|
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_setting(self, key: str, value: Any) -> None:
|
||||||
|
"""
|
||||||
|
更新设置项
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: 设置键
|
||||||
|
value: 设置值
|
||||||
|
"""
|
||||||
|
self.settings[key] = value
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def add_metadata(self, key: str, value: Any) -> None:
|
||||||
|
"""
|
||||||
|
添加元数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: 元数据键
|
||||||
|
value: 元数据值
|
||||||
|
"""
|
||||||
|
self.metadata[key] = value
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def add_tag(self, tag: str) -> None:
|
||||||
|
"""
|
||||||
|
添加标签
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tag: 标签名称
|
||||||
|
"""
|
||||||
|
if 'tags' not in self.metadata:
|
||||||
|
self.metadata['tags'] = []
|
||||||
|
|
||||||
|
if tag not in self.metadata['tags']:
|
||||||
|
self.metadata['tags'].append(tag)
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def remove_tag(self, tag: str) -> bool:
|
||||||
|
"""
|
||||||
|
移除标签
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tag: 标签名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否成功移除
|
||||||
|
"""
|
||||||
|
if 'tags' in self.metadata and tag in self.metadata['tags']:
|
||||||
|
self.metadata['tags'].remove(tag)
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def extend_expiry(self, days: int = 7) -> None:
|
||||||
|
"""
|
||||||
|
延长过期时间
|
||||||
|
|
||||||
|
Args:
|
||||||
|
days: 延长天数
|
||||||
|
"""
|
||||||
|
self.expires_at = datetime.now() + timedelta(days=days)
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def is_expired(self) -> bool:
|
||||||
|
"""
|
||||||
|
检查是否已过期
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否已过期
|
||||||
|
"""
|
||||||
|
return datetime.now() > self.expires_at
|
||||||
|
|
||||||
|
def is_active(self) -> bool:
|
||||||
|
"""
|
||||||
|
检查是否处于活跃状态
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否活跃
|
||||||
|
"""
|
||||||
|
return self.status == SessionStatus.ACTIVE.value and not self.is_expired()
|
||||||
|
|
||||||
|
def is_completed(self) -> bool:
|
||||||
|
"""
|
||||||
|
检查是否已完成
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否已完成
|
||||||
|
"""
|
||||||
|
return self.status == SessionStatus.COMPLETED.value or self.current_step >= self.total_steps
|
||||||
|
|
||||||
|
def get_progress_percentage(self) -> float:
|
||||||
|
"""
|
||||||
|
获取进度百分比
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: 进度百分比 (0-100)
|
||||||
|
"""
|
||||||
|
if self.total_steps <= 0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
return min(100.0, (self.current_step / self.total_steps) * 100.0)
|
||||||
|
|
||||||
|
def get_remaining_steps(self) -> int:
|
||||||
|
"""
|
||||||
|
获取剩余步骤数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 剩余步骤数
|
||||||
|
"""
|
||||||
|
return max(0, self.total_steps - self.current_step)
|
||||||
|
|
||||||
|
def get_session_duration(self) -> timedelta:
|
||||||
|
"""
|
||||||
|
获取会话持续时间
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
timedelta: 持续时间
|
||||||
|
"""
|
||||||
|
return self.updated_at - self.created_at
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""
|
||||||
|
字符串表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 字符串描述
|
||||||
|
"""
|
||||||
|
return f"SessionModel(id={self.session_id}, user={self.user_id}, step={self.current_step}/{self.total_steps}, status={self.status})"
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
"""
|
||||||
|
详细字符串表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 详细描述
|
||||||
|
"""
|
||||||
|
return (f"SessionModel(session_id={self.session_id}, user_id={self.user_id}, "
|
||||||
|
f"type={self.session_type}, title='{self.title}', status={self.status}, "
|
||||||
|
f"step={self.current_step}/{self.total_steps}, created={self.created_at})")
|
||||||
397
models/session_model.py
Normal file
397
models/session_model.py
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
会话数据模型
|
||||||
|
|
||||||
|
定义会话的数据结构和验证逻辑
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, List, Optional, Any
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class SessionStatus(Enum):
|
||||||
|
"""会话状态枚举"""
|
||||||
|
QUEUED = "queued" # 排队中
|
||||||
|
PENDING = "pending" # 等待中
|
||||||
|
ACTIVE = "active" # 活跃状态
|
||||||
|
PAUSED = "paused" # 暂停状态
|
||||||
|
COMPLETED = "completed" # 已完成
|
||||||
|
CANCELLED = "cancelled" # 已取消
|
||||||
|
ERROR = "error" # 错误状态
|
||||||
|
|
||||||
|
class SessionType(Enum):
|
||||||
|
"""会话类型枚举"""
|
||||||
|
SCRIPTWRITING = "scriptwriting" # 编剧创作
|
||||||
|
CONSULTATION = "consultation" # 咨询对话
|
||||||
|
REVIEW = "review" # 剧本评审
|
||||||
|
COLLABORATION = "collaboration" # 协作创作
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SessionModel:
|
||||||
|
"""
|
||||||
|
会话数据模型
|
||||||
|
|
||||||
|
定义会话的完整数据结构
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 基础信息
|
||||||
|
user_id: str
|
||||||
|
session_type: str = SessionType.SCRIPTWRITING.value
|
||||||
|
title: str = ""
|
||||||
|
description: str = ""
|
||||||
|
# 原始剧本内容
|
||||||
|
original_script: str = ""
|
||||||
|
|
||||||
|
# 状态信息
|
||||||
|
status: str = SessionStatus.ACTIVE.value
|
||||||
|
current_step: int = 1
|
||||||
|
total_steps: int = 6
|
||||||
|
|
||||||
|
# 配置信息
|
||||||
|
settings: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
# 时间信息
|
||||||
|
created_at: datetime = field(default_factory=datetime.now)
|
||||||
|
updated_at: datetime = field(default_factory=datetime.now)
|
||||||
|
expires_at: datetime = field(default_factory=lambda: datetime.now() + timedelta(days=7))
|
||||||
|
|
||||||
|
# 可选字段
|
||||||
|
session_id: Optional[str] = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
"""初始化后处理"""
|
||||||
|
# 验证数据
|
||||||
|
self.validate()
|
||||||
|
|
||||||
|
# 设置默认值
|
||||||
|
if not self.title:
|
||||||
|
self.title = f"编剧会话 - {self.created_at.strftime('%Y-%m-%d %H:%M')}"
|
||||||
|
|
||||||
|
# 确保设置字典存在必要的键
|
||||||
|
default_settings = {
|
||||||
|
'auto_save': True,
|
||||||
|
'step_timeout': 1800, # 30分钟
|
||||||
|
'max_retries': 3,
|
||||||
|
'language': 'zh-CN',
|
||||||
|
'genre': 'drama',
|
||||||
|
'style': 'modern'
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value in default_settings.items():
|
||||||
|
if key not in self.settings:
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
# 确保元数据字典存在
|
||||||
|
if 'tags' not in self.metadata:
|
||||||
|
self.metadata['tags'] = []
|
||||||
|
if 'source' not in self.metadata:
|
||||||
|
self.metadata['source'] = 'web_interface'
|
||||||
|
|
||||||
|
def validate(self) -> bool:
|
||||||
|
"""
|
||||||
|
验证数据有效性
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否有效
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: 数据无效时抛出异常
|
||||||
|
"""
|
||||||
|
# 验证用户ID
|
||||||
|
if not self.user_id or not isinstance(self.user_id, str):
|
||||||
|
raise ValueError("用户ID不能为空且必须是字符串")
|
||||||
|
|
||||||
|
# 验证会话类型
|
||||||
|
valid_types = [t.value for t in SessionType]
|
||||||
|
if self.session_type not in valid_types:
|
||||||
|
raise ValueError(f"无效的会话类型: {self.session_type},有效值: {valid_types}")
|
||||||
|
|
||||||
|
# 验证状态
|
||||||
|
valid_statuses = [s.value for s in SessionStatus]
|
||||||
|
if self.status not in valid_statuses:
|
||||||
|
raise ValueError(f"无效的会话状态: {self.status},有效值: {valid_statuses}")
|
||||||
|
|
||||||
|
# 验证步骤
|
||||||
|
if not isinstance(self.current_step, int) or self.current_step < 1:
|
||||||
|
raise ValueError("当前步骤必须是大于0的整数")
|
||||||
|
|
||||||
|
if not isinstance(self.total_steps, int) or self.total_steps < 1:
|
||||||
|
raise ValueError("总步骤数必须是大于0的整数")
|
||||||
|
|
||||||
|
if self.current_step > self.total_steps:
|
||||||
|
raise ValueError("当前步骤不能超过总步骤数")
|
||||||
|
|
||||||
|
# 验证时间
|
||||||
|
if self.updated_at < self.created_at:
|
||||||
|
raise ValueError("更新时间不能早于创建时间")
|
||||||
|
|
||||||
|
if self.expires_at <= self.created_at:
|
||||||
|
raise ValueError("过期时间必须晚于创建时间")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
转换为字典格式
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 字典数据
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'user_id': self.user_id,
|
||||||
|
'session_type': self.session_type,
|
||||||
|
'title': self.title,
|
||||||
|
'description': self.description,
|
||||||
|
'original_script': self.original_script,
|
||||||
|
'status': self.status,
|
||||||
|
'current_step': self.current_step,
|
||||||
|
'total_steps': self.total_steps,
|
||||||
|
'settings': self.settings.copy(),
|
||||||
|
'metadata': self.metadata.copy(),
|
||||||
|
'created_at': self.created_at,
|
||||||
|
'updated_at': self.updated_at,
|
||||||
|
'expires_at': self.expires_at
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.session_id:
|
||||||
|
data['session_id'] = self.session_id
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: Dict[str, Any]) -> 'SessionModel':
|
||||||
|
"""
|
||||||
|
从字典创建模型实例
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: 字典数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SessionModel: 模型实例
|
||||||
|
"""
|
||||||
|
# 提取必需字段
|
||||||
|
user_id = data.get('user_id')
|
||||||
|
if not user_id:
|
||||||
|
raise ValueError("缺少必需字段: user_id")
|
||||||
|
|
||||||
|
# 创建实例
|
||||||
|
instance = cls(
|
||||||
|
user_id=user_id,
|
||||||
|
session_type=data.get('session_type', SessionType.SCRIPTWRITING.value),
|
||||||
|
title=data.get('title', ''),
|
||||||
|
description=data.get('description', ''),
|
||||||
|
status=data.get('status', SessionStatus.ACTIVE.value),
|
||||||
|
current_step=data.get('current_step', 1),
|
||||||
|
total_steps=data.get('total_steps', 6),
|
||||||
|
settings=data.get('settings', {}),
|
||||||
|
metadata=data.get('metadata', {})
|
||||||
|
)
|
||||||
|
|
||||||
|
# 设置时间字段
|
||||||
|
if 'created_at' in data:
|
||||||
|
instance.created_at = data['created_at']
|
||||||
|
if 'updated_at' in data:
|
||||||
|
instance.updated_at = data['updated_at']
|
||||||
|
if 'expires_at' in data:
|
||||||
|
instance.expires_at = data['expires_at']
|
||||||
|
|
||||||
|
# 设置可选字段
|
||||||
|
if 'session_id' in data:
|
||||||
|
instance.session_id = data['session_id']
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def update_status(self, new_status: str) -> None:
|
||||||
|
"""
|
||||||
|
更新会话状态
|
||||||
|
|
||||||
|
Args:
|
||||||
|
new_status: 新状态
|
||||||
|
"""
|
||||||
|
valid_statuses = [s.value for s in SessionStatus]
|
||||||
|
if new_status not in valid_statuses:
|
||||||
|
raise ValueError(f"无效的会话状态: {new_status}")
|
||||||
|
|
||||||
|
self.status = new_status
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def advance_step(self) -> bool:
|
||||||
|
"""
|
||||||
|
推进到下一步
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否成功推进
|
||||||
|
"""
|
||||||
|
if self.current_step < self.total_steps:
|
||||||
|
self.current_step += 1
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
# 如果到达最后一步,标记为完成
|
||||||
|
if self.current_step == self.total_steps:
|
||||||
|
self.status = SessionStatus.COMPLETED.value
|
||||||
|
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def reset_step(self, step_number: int) -> bool:
|
||||||
|
"""
|
||||||
|
重置到指定步骤
|
||||||
|
|
||||||
|
Args:
|
||||||
|
step_number: 目标步骤号
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否成功重置
|
||||||
|
"""
|
||||||
|
if 1 <= step_number <= self.total_steps:
|
||||||
|
self.current_step = step_number
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
# 如果从完成状态回退,更新状态
|
||||||
|
if self.status == SessionStatus.COMPLETED.value and step_number < self.total_steps:
|
||||||
|
self.status = SessionStatus.ACTIVE.value
|
||||||
|
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_setting(self, key: str, value: Any) -> None:
|
||||||
|
"""
|
||||||
|
更新设置项
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: 设置键
|
||||||
|
value: 设置值
|
||||||
|
"""
|
||||||
|
self.settings[key] = value
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def add_metadata(self, key: str, value: Any) -> None:
|
||||||
|
"""
|
||||||
|
添加元数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: 元数据键
|
||||||
|
value: 元数据值
|
||||||
|
"""
|
||||||
|
self.metadata[key] = value
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def add_tag(self, tag: str) -> None:
|
||||||
|
"""
|
||||||
|
添加标签
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tag: 标签名称
|
||||||
|
"""
|
||||||
|
if 'tags' not in self.metadata:
|
||||||
|
self.metadata['tags'] = []
|
||||||
|
|
||||||
|
if tag not in self.metadata['tags']:
|
||||||
|
self.metadata['tags'].append(tag)
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def remove_tag(self, tag: str) -> bool:
|
||||||
|
"""
|
||||||
|
移除标签
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tag: 标签名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否成功移除
|
||||||
|
"""
|
||||||
|
if 'tags' in self.metadata and tag in self.metadata['tags']:
|
||||||
|
self.metadata['tags'].remove(tag)
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def extend_expiry(self, days: int = 7) -> None:
|
||||||
|
"""
|
||||||
|
延长过期时间
|
||||||
|
|
||||||
|
Args:
|
||||||
|
days: 延长天数
|
||||||
|
"""
|
||||||
|
self.expires_at = datetime.now() + timedelta(days=days)
|
||||||
|
self.updated_at = datetime.now()
|
||||||
|
|
||||||
|
def is_expired(self) -> bool:
|
||||||
|
"""
|
||||||
|
检查是否已过期
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否已过期
|
||||||
|
"""
|
||||||
|
return datetime.now() > self.expires_at
|
||||||
|
|
||||||
|
def is_active(self) -> bool:
|
||||||
|
"""
|
||||||
|
检查是否处于活跃状态
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否活跃
|
||||||
|
"""
|
||||||
|
return self.status == SessionStatus.ACTIVE.value and not self.is_expired()
|
||||||
|
|
||||||
|
def is_completed(self) -> bool:
|
||||||
|
"""
|
||||||
|
检查是否已完成
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否已完成
|
||||||
|
"""
|
||||||
|
return self.status == SessionStatus.COMPLETED.value or self.current_step >= self.total_steps
|
||||||
|
|
||||||
|
def get_progress_percentage(self) -> float:
|
||||||
|
"""
|
||||||
|
获取进度百分比
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
float: 进度百分比 (0-100)
|
||||||
|
"""
|
||||||
|
if self.total_steps <= 0:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
return min(100.0, (self.current_step / self.total_steps) * 100.0)
|
||||||
|
|
||||||
|
def get_remaining_steps(self) -> int:
|
||||||
|
"""
|
||||||
|
获取剩余步骤数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: 剩余步骤数
|
||||||
|
"""
|
||||||
|
return max(0, self.total_steps - self.current_step)
|
||||||
|
|
||||||
|
def get_session_duration(self) -> timedelta:
|
||||||
|
"""
|
||||||
|
获取会话持续时间
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
timedelta: 持续时间
|
||||||
|
"""
|
||||||
|
return self.updated_at - self.created_at
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""
|
||||||
|
字符串表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 字符串描述
|
||||||
|
"""
|
||||||
|
return f"SessionModel(id={self.session_id}, user={self.user_id}, step={self.current_step}/{self.total_steps}, status={self.status})"
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
"""
|
||||||
|
详细字符串表示
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 详细描述
|
||||||
|
"""
|
||||||
|
return (f"SessionModel(session_id={self.session_id}, user_id={self.user_id}, "
|
||||||
|
f"type={self.session_type}, title='{self.title}', status={self.status}, "
|
||||||
|
f"step={self.current_step}/{self.total_steps}, created={self.created_at})")
|
||||||
10
pyrightconfig.json
Normal file
10
pyrightconfig.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"typeCheckingMode": "basic",
|
||||||
|
"reportUnknownMemberType": false,
|
||||||
|
"reportUnknownVariableType": false,
|
||||||
|
"reportUnknownArgumentType": false,
|
||||||
|
"reportMissingParameterType": false,
|
||||||
|
"reportMissingTypeArgument": false,
|
||||||
|
"reportUnknownParameterType": false,
|
||||||
|
"reportImplicitRelativeImport": false
|
||||||
|
}
|
||||||
23
requirements.txt
Normal file
23
requirements.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Flask
|
||||||
|
Flask-Cors
|
||||||
|
pymongo
|
||||||
|
python-dotenv
|
||||||
|
PyJWT
|
||||||
|
Werkzeug
|
||||||
|
dnspython
|
||||||
|
requests
|
||||||
|
oss2
|
||||||
|
PyPDF2
|
||||||
|
PyMuPDF
|
||||||
|
python-docx
|
||||||
|
openai
|
||||||
|
Pillow
|
||||||
|
tos
|
||||||
|
volcengine-python-sdk[ark]
|
||||||
|
volcengine[ark]
|
||||||
|
PyMySQL
|
||||||
|
DBUtils
|
||||||
|
PyMuPDF
|
||||||
|
langchain-community
|
||||||
|
langgraph
|
||||||
|
langgraph-checkpoint-mongodb
|
||||||
4
routes/__init__.py
Normal file
4
routes/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# routes/__init__.py
|
||||||
|
"""
|
||||||
|
路由模块初始化文件
|
||||||
|
"""
|
||||||
11
routes/langgraph_routes.py
Normal file
11
routes/langgraph_routes.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
from handlers.langgraph_handler import run_langgraph, get_task_status, resume_task, visualize_graph
|
||||||
|
|
||||||
|
# 创建蓝图
|
||||||
|
bp = Blueprint('langgraph', __name__, url_prefix='/api/v1/langgraph')
|
||||||
|
|
||||||
|
# 定义路由
|
||||||
|
bp.add_url_rule('/run', view_func=run_langgraph, methods=['POST'])
|
||||||
|
bp.add_url_rule('/status/<thread_id>', view_func=get_task_status, methods=['GET'])
|
||||||
|
bp.add_url_rule('/resume/<thread_id>', view_func=resume_task, methods=['POST'])
|
||||||
|
bp.add_url_rule('/visualize/<thread_id>', view_func=visualize_graph, methods=['GET'])
|
||||||
4
tools/__init__.py
Normal file
4
tools/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# tools/__init__.py
|
||||||
|
"""
|
||||||
|
工具模块初始化文件
|
||||||
|
"""
|
||||||
11
tools/database/__init__.py
Normal file
11
tools/database/__init__.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# tools/database/__init__.py
|
||||||
|
"""
|
||||||
|
数据库工具模块初始化文件
|
||||||
|
"""
|
||||||
|
# 从mongo.py导入常用的类或函数,方便其他模块导入
|
||||||
|
try:
|
||||||
|
from .mongo import client, mainDB
|
||||||
|
__all__ = ['client', 'mainDB']
|
||||||
|
except ImportError:
|
||||||
|
# 如果mongo模块无法导入,不抛出异常,保持包的基本功能
|
||||||
|
pass
|
||||||
106
tools/database/mongo.py
Normal file
106
tools/database/mongo.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
from pymongo import MongoClient
|
||||||
|
import config
|
||||||
|
|
||||||
|
from pymongo.monitoring import (CommandListener, ServerListener, ConnectionPoolListener,
|
||||||
|
CommandSucceededEvent, CommandFailedEvent,
|
||||||
|
ServerHeartbeatStartedEvent, ServerHeartbeatSucceededEvent, ServerHeartbeatFailedEvent,
|
||||||
|
ConnectionCreatedEvent, ConnectionClosedEvent, ConnectionCheckOutStartedEvent,
|
||||||
|
ConnectionCheckedOutEvent, ConnectionCheckOutFailedEvent, ConnectionCheckedInEvent)
|
||||||
|
|
||||||
|
# 命令事件监听器
|
||||||
|
class MyCommandListener(CommandListener):
|
||||||
|
def started(self, event):
|
||||||
|
# print(f"Command {event.command_name} started on {event.connection_id}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def succeeded(self, event: CommandSucceededEvent):
|
||||||
|
# print(f"Command {event.command_name} succeeded in {event.duration_micros} microseconds")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def failed(self, event: CommandFailedEvent):
|
||||||
|
print(f"Command {event.command_name} failed with error: {event.failure}")
|
||||||
|
|
||||||
|
# 服务器事件监听器
|
||||||
|
class MyServerListener(ServerListener):
|
||||||
|
def opened(self, event):
|
||||||
|
print(f"Server {event.server_address} opened")
|
||||||
|
|
||||||
|
def description_changed(self, event):
|
||||||
|
# print(f"Server {event.previous_description.address} description changed to {event.new_description.address}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def closed(self, event):
|
||||||
|
print(f"Server {event.server_address} closed")
|
||||||
|
|
||||||
|
def heartbeat_started(self, event: ServerHeartbeatStartedEvent):
|
||||||
|
# print(f"Heartbeat started on server with id: {event.connection_id}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def heartbeat_succeeded(self, event: ServerHeartbeatSucceededEvent):
|
||||||
|
# print(f"Heartbeat succeeded on server with id: {event.connection_id}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def heartbeat_failed(self, event: ServerHeartbeatFailedEvent):
|
||||||
|
print(f"Heartbeat failed on server with id: {event.connection_id} with error: {event.reply}")
|
||||||
|
|
||||||
|
# 连接池事件监听器
|
||||||
|
class MyPoolListener(ConnectionPoolListener):
|
||||||
|
def pool_created(self, event):
|
||||||
|
print(f"Connection pool created for {event.address}")
|
||||||
|
|
||||||
|
def pool_cleared(self, event):
|
||||||
|
print(f"Connection pool cleared for {event.address}")
|
||||||
|
|
||||||
|
def pool_closed(self, event):
|
||||||
|
print(f"Connection pool closed for {event.address}")
|
||||||
|
|
||||||
|
def connection_created(self, event: ConnectionCreatedEvent):
|
||||||
|
print(f"Connection {event.connection_id} created for {event.address}")
|
||||||
|
|
||||||
|
def connection_ready(self, event):
|
||||||
|
print(f"Connection {event.connection_id} ready for {event.address}")
|
||||||
|
|
||||||
|
def pool_ready(self, event):
|
||||||
|
print(f"Connection pool ready for {event.address}")
|
||||||
|
|
||||||
|
def connection_closed(self, event: ConnectionClosedEvent):
|
||||||
|
print(f"Connection {event.connection_id} closed for {event.address}, reason: {event.reason}")
|
||||||
|
|
||||||
|
def connection_check_out_started(self, event: ConnectionCheckOutStartedEvent):
|
||||||
|
# print(f"Connection check out started for {event.address}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def connection_check_out_failed(self, event: ConnectionCheckOutFailedEvent):
|
||||||
|
print(f"Connection check out failed for {event.address}, reason: {event.reason}")
|
||||||
|
|
||||||
|
def connection_checked_out(self, event: ConnectionCheckedOutEvent):
|
||||||
|
# print(f"Connection {event.connection_id} checked out for {event.address}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def connection_checked_in(self, event: ConnectionCheckedInEvent):
|
||||||
|
# print(f"Connection {event.connection_id} checked in for {event.address}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# 实例化监听器
|
||||||
|
command_listener = MyCommandListener()
|
||||||
|
server_listener = MyServerListener()
|
||||||
|
pool_listener = MyPoolListener()
|
||||||
|
|
||||||
|
all_event_listeners = [command_listener, server_listener, pool_listener]
|
||||||
|
|
||||||
|
MONGO_URI = config.MONGO_URI
|
||||||
|
DB_NAME = config.MONGO_MAIN_DB_NAME
|
||||||
|
|
||||||
|
# 创建MongoDB客户端连接
|
||||||
|
try:
|
||||||
|
# 实例化MongoClient时传入事件监听器
|
||||||
|
client = MongoClient(MONGO_URI, event_listeners=all_event_listeners, serverSelectionTimeoutMS=5000) # 设置5秒超时
|
||||||
|
mainDB = client[DB_NAME]
|
||||||
|
# 主动检查连接状态
|
||||||
|
client.admin.command('ping')
|
||||||
|
success_message = f"\033[92m成功连接到MongoDB: {DB_NAME},事件监听器已激活。\033[0m"
|
||||||
|
print(success_message)
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"\033[91m数据库连接失败: {MONGO_URI},请检查MongoDB服务是否已启动。\033[0m"
|
||||||
|
print(error_message)
|
||||||
30
tools/database/mongodb_memory.py
Normal file
30
tools/database/mongodb_memory.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
"""工作流记忆管理模块
|
||||||
|
|
||||||
|
该模块负责管理智能编剧系统工作流的记忆存储和检索。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from typing import Dict, Any, List, Optional
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
from database import client # type: ignore
|
||||||
|
from langgraph.checkpoint.mongodb import MongoDBSaver
|
||||||
|
|
||||||
|
# 添加项目根目录到路径
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
|
||||||
|
from agentgraph.utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
DB_NAME = "langgraph_memory_db"
|
||||||
|
|
||||||
|
class WorkflowMemory:
|
||||||
|
"""工作流记忆管理类
|
||||||
|
|
||||||
|
负责管理工作流执行过程中的状态存储、检索和历史记录。
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""初始化工作流记忆管理器"""
|
||||||
|
self.memory = MongoDBSaver(client, db_name=DB_NAME)
|
||||||
9
tools/llm/__init__.py
Normal file
9
tools/llm/__init__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# tools/llm/__init__.py
|
||||||
|
"""
|
||||||
|
大语言模型工具模块初始化文件
|
||||||
|
"""
|
||||||
|
from .huoshan_langchain import HuoshanChatModel
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'HuoshanChatModel'
|
||||||
|
]
|
||||||
137
tools/llm/huoshan_langchain.py
Normal file
137
tools/llm/huoshan_langchain.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
from typing import Any, Dict, Iterator, List, Optional
|
||||||
|
from langchain_core.callbacks.manager import CallbackManagerForLLMRun
|
||||||
|
from langchain_core.language_models.chat_models import BaseChatModel
|
||||||
|
from langchain_core.messages import BaseMessage, AIMessage, HumanMessage, SystemMessage
|
||||||
|
from langchain_core.outputs import ChatGeneration, ChatResult
|
||||||
|
from pydantic import Field
|
||||||
|
from api.huoshan import HuoshanAPI # 导入你现有的API类
|
||||||
|
|
||||||
|
|
||||||
|
class HuoshanChatModel(BaseChatModel):
|
||||||
|
"""火山引擎聊天模型的LangChain封装"""
|
||||||
|
|
||||||
|
# 模型配置参数
|
||||||
|
model_name: str = Field(default="doubao-seed-1.6-250615", description="模型名称")
|
||||||
|
temperature: float = Field(default=0.6, description="温度参数")
|
||||||
|
max_tokens: int = Field(default=16384, description="最大token数")
|
||||||
|
|
||||||
|
# 内部API实例
|
||||||
|
_api: Optional[HuoshanAPI] = None
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
# 初始化火山引擎API实例
|
||||||
|
self._api = HuoshanAPI()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _llm_type(self) -> str:
|
||||||
|
"""返回LLM类型标识"""
|
||||||
|
return "huoshan_chat"
|
||||||
|
|
||||||
|
def _convert_messages_to_prompt(self, messages: List[BaseMessage]) -> tuple[str, str]:
|
||||||
|
"""将LangChain消息格式转换为API所需的prompt和system格式"""
|
||||||
|
system_message = ""
|
||||||
|
user_messages = []
|
||||||
|
|
||||||
|
for message in messages:
|
||||||
|
if isinstance(message, SystemMessage):
|
||||||
|
system_message = message.content or ""
|
||||||
|
elif isinstance(message, HumanMessage):
|
||||||
|
user_messages.append(message.content)
|
||||||
|
elif isinstance(message, AIMessage):
|
||||||
|
# 如果需要支持多轮对话,可以在这里处理
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 合并用户消息
|
||||||
|
prompt = "\n".join(user_messages) if user_messages else ""
|
||||||
|
|
||||||
|
return prompt, str(system_message)
|
||||||
|
|
||||||
|
def _generate(
|
||||||
|
self,
|
||||||
|
messages: List[BaseMessage],
|
||||||
|
stop: Optional[List[str]] = None,
|
||||||
|
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> ChatResult:
|
||||||
|
"""生成聊天回复"""
|
||||||
|
if not self._api:
|
||||||
|
raise ValueError("HuoshanAPI未正确初始化")
|
||||||
|
|
||||||
|
# 转换消息格式
|
||||||
|
prompt, system = self._convert_messages_to_prompt(messages)
|
||||||
|
|
||||||
|
# 合并参数
|
||||||
|
generation_kwargs = {
|
||||||
|
"model": kwargs.get("model", self.model_name),
|
||||||
|
"temperature": kwargs.get("temperature", self.temperature),
|
||||||
|
"system": system
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 调用你的API
|
||||||
|
response_text = self._api.get_chat_response(
|
||||||
|
prompt=prompt,
|
||||||
|
**generation_kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建AI消息
|
||||||
|
message = AIMessage(content=response_text)
|
||||||
|
|
||||||
|
# 创建生成结果
|
||||||
|
generation = ChatGeneration(message=message)
|
||||||
|
|
||||||
|
return ChatResult(generations=[generation])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"调用火山引擎API失败: {str(e)}")
|
||||||
|
|
||||||
|
def _stream(
|
||||||
|
self,
|
||||||
|
messages: List[BaseMessage],
|
||||||
|
stop: Optional[List[str]] = None,
|
||||||
|
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Iterator[ChatGeneration]:
|
||||||
|
"""流式生成聊天回复"""
|
||||||
|
if not self._api:
|
||||||
|
raise ValueError("HuoshanAPI未正确初始化")
|
||||||
|
|
||||||
|
# 转换消息格式
|
||||||
|
prompt, system = self._convert_messages_to_prompt(messages)
|
||||||
|
|
||||||
|
# 合并参数
|
||||||
|
generation_kwargs = {
|
||||||
|
"model": kwargs.get("model", self.model_name),
|
||||||
|
"temperature": kwargs.get("temperature", self.temperature),
|
||||||
|
"system": system
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 调用流式API
|
||||||
|
for chunk in self._api.get_chat_response_stream(
|
||||||
|
prompt=prompt,
|
||||||
|
**generation_kwargs
|
||||||
|
):
|
||||||
|
if chunk:
|
||||||
|
# 创建增量消息
|
||||||
|
message = AIMessage(content=chunk)
|
||||||
|
generation = ChatGeneration(message=message)
|
||||||
|
|
||||||
|
# 如果有回调管理器,通知新token
|
||||||
|
if run_manager:
|
||||||
|
run_manager.on_llm_new_token(chunk)
|
||||||
|
|
||||||
|
yield generation
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise ValueError(f"调用火山引擎流式API失败: {str(e)}")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _identifying_params(self) -> Dict[str, Any]:
|
||||||
|
"""返回用于标识模型的参数"""
|
||||||
|
return {
|
||||||
|
"model_name": self.model_name,
|
||||||
|
"temperature": self.temperature,
|
||||||
|
"max_tokens": self.max_tokens,
|
||||||
|
}
|
||||||
0
utils/__init__.py
Normal file
0
utils/__init__.py
Normal file
655
utils/formatters.py
Normal file
655
utils/formatters.py
Normal file
@ -0,0 +1,655 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
格式化器模块
|
||||||
|
|
||||||
|
提供响应格式化、数据转换和输出格式化功能
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from datetime import datetime, date
|
||||||
|
from typing import Dict, List, Optional, Any, Union
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
class ResponseFormatter:
|
||||||
|
"""响应格式化器"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def success_response(data: Any = None, message: str = "操作成功",
|
||||||
|
code: int = 200, extra: Dict[str, Any] = {}) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建成功响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: 响应数据
|
||||||
|
message: 响应消息
|
||||||
|
code: 状态码
|
||||||
|
extra: 额外信息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的响应
|
||||||
|
"""
|
||||||
|
response = {
|
||||||
|
"success": True,
|
||||||
|
"code": code,
|
||||||
|
"message": message,
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"data": data
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra:
|
||||||
|
response.update(extra)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def error_response(message: str = "操作失败", code: int = 400,
|
||||||
|
error_type: str = "ValidationError",
|
||||||
|
details: Dict[str, Any] = {}) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建错误响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: 错误消息
|
||||||
|
code: 错误码
|
||||||
|
error_type: 错误类型
|
||||||
|
details: 错误详情
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的错误响应
|
||||||
|
"""
|
||||||
|
response = {
|
||||||
|
"success": False,
|
||||||
|
"code": code,
|
||||||
|
"message": message,
|
||||||
|
"error_type": error_type,
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
if details:
|
||||||
|
response["details"] = details
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def paginated_response(items: List[Any], total: int, page: int,
|
||||||
|
page_size: int, message: str = "查询成功") -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建分页响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
items: 数据项列表
|
||||||
|
total: 总数量
|
||||||
|
page: 当前页码
|
||||||
|
page_size: 页大小
|
||||||
|
message: 响应消息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的分页响应
|
||||||
|
"""
|
||||||
|
total_pages = (total + page_size - 1) // page_size
|
||||||
|
|
||||||
|
pagination = {
|
||||||
|
"current_page": page,
|
||||||
|
"page_size": page_size,
|
||||||
|
"total_items": total,
|
||||||
|
"total_pages": total_pages,
|
||||||
|
"has_next": page < total_pages,
|
||||||
|
"has_prev": page > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseFormatter.success_response(
|
||||||
|
data={
|
||||||
|
"items": items,
|
||||||
|
"pagination": pagination
|
||||||
|
},
|
||||||
|
message=message
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def stream_response(data: Any, event_type: str = "data",
|
||||||
|
event_id: str = "") -> str:
|
||||||
|
"""
|
||||||
|
创建流式响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: 响应数据
|
||||||
|
event_type: 事件类型
|
||||||
|
event_id: 事件ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: SSE格式的响应
|
||||||
|
"""
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
if event_id:
|
||||||
|
lines.append(f"id: {event_id}")
|
||||||
|
|
||||||
|
lines.append(f"event: {event_type}")
|
||||||
|
|
||||||
|
# 将数据转换为JSON字符串
|
||||||
|
if isinstance(data, (dict, list)):
|
||||||
|
data_str = json.dumps(data, ensure_ascii=False, cls=DateTimeEncoder)
|
||||||
|
else:
|
||||||
|
data_str = str(data)
|
||||||
|
|
||||||
|
lines.append(f"data: {data_str}")
|
||||||
|
lines.append("") # 空行表示事件结束
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validation_error_response(errors: List[str],
|
||||||
|
warnings: List[str] = []) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建验证错误响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
errors: 错误列表
|
||||||
|
warnings: 警告列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的验证错误响应
|
||||||
|
"""
|
||||||
|
details = {"errors": errors}
|
||||||
|
|
||||||
|
if warnings:
|
||||||
|
details["warnings"] = warnings
|
||||||
|
|
||||||
|
return ResponseFormatter.error_response(
|
||||||
|
message="数据验证失败",
|
||||||
|
code=422,
|
||||||
|
error_type="ValidationError",
|
||||||
|
details=details
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def not_found_response(resource: str = "资源") -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建资源未找到响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
resource: 资源名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的未找到响应
|
||||||
|
"""
|
||||||
|
return ResponseFormatter.error_response(
|
||||||
|
message=f"{resource}未找到",
|
||||||
|
code=404,
|
||||||
|
error_type="NotFoundError"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unauthorized_response(message: str = "未授权访问") -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建未授权响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: 错误消息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的未授权响应
|
||||||
|
"""
|
||||||
|
return ResponseFormatter.error_response(
|
||||||
|
message=message,
|
||||||
|
code=401,
|
||||||
|
error_type="UnauthorizedError"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def forbidden_response(message: str = "禁止访问") -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建禁止访问响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: 错误消息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的禁止访问响应
|
||||||
|
"""
|
||||||
|
return ResponseFormatter.error_response(
|
||||||
|
message=message,
|
||||||
|
code=403,
|
||||||
|
error_type="ForbiddenError"
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def rate_limit_response(retry_after: int = 60) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建限流响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
retry_after: 重试等待时间(秒)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的限流响应
|
||||||
|
"""
|
||||||
|
return ResponseFormatter.error_response(
|
||||||
|
message="请求过于频繁,请稍后重试",
|
||||||
|
code=429,
|
||||||
|
error_type="RateLimitError",
|
||||||
|
details={"retry_after": retry_after}
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def server_error_response(message: str = "服务器内部错误") -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
创建服务器错误响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: 错误消息
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的服务器错误响应
|
||||||
|
"""
|
||||||
|
return ResponseFormatter.error_response(
|
||||||
|
message=message,
|
||||||
|
code=500,
|
||||||
|
error_type="InternalServerError"
|
||||||
|
)
|
||||||
|
|
||||||
|
class ErrorFormatter:
|
||||||
|
"""错误格式化器"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_exception(exception: Exception, include_traceback: bool = False) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
格式化异常信息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
exception: 异常对象
|
||||||
|
include_traceback: 是否包含堆栈跟踪
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的异常信息
|
||||||
|
"""
|
||||||
|
error_info = {
|
||||||
|
"type": exception.__class__.__name__,
|
||||||
|
"message": str(exception),
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
if include_traceback:
|
||||||
|
import traceback
|
||||||
|
error_info["traceback"] = traceback.format_exc()
|
||||||
|
|
||||||
|
return error_info
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_validation_errors(errors: List[str], field_errors: Dict[str, List[str]] = {}) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
格式化验证错误
|
||||||
|
|
||||||
|
Args:
|
||||||
|
errors: 通用错误列表
|
||||||
|
field_errors: 字段特定错误字典
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的验证错误
|
||||||
|
"""
|
||||||
|
formatted = {
|
||||||
|
"general_errors": errors,
|
||||||
|
"error_count": len(errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if field_errors:
|
||||||
|
formatted["field_errors"] = field_errors
|
||||||
|
formatted["error_count"] += sum(len(errs) for errs in field_errors.values())
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_database_error(error: Exception) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
格式化数据库错误
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error: 数据库异常
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的数据库错误
|
||||||
|
"""
|
||||||
|
error_message = str(error)
|
||||||
|
|
||||||
|
# 根据错误类型提供更友好的消息
|
||||||
|
if "duplicate key" in error_message.lower():
|
||||||
|
user_message = "数据已存在,请检查唯一性约束"
|
||||||
|
elif "foreign key" in error_message.lower():
|
||||||
|
user_message = "关联数据不存在,请检查数据完整性"
|
||||||
|
elif "not null" in error_message.lower():
|
||||||
|
user_message = "必填字段不能为空"
|
||||||
|
elif "timeout" in error_message.lower():
|
||||||
|
user_message = "数据库操作超时,请稍后重试"
|
||||||
|
else:
|
||||||
|
user_message = "数据库操作失败"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": "DatabaseError",
|
||||||
|
"user_message": user_message,
|
||||||
|
"technical_message": error_message,
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataFormatter:
|
||||||
|
"""数据格式化器"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_session_data(session_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
格式化会话数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session_data: 原始会话数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的会话数据
|
||||||
|
"""
|
||||||
|
formatted = {
|
||||||
|
"session_id": session_data.get("session_id"),
|
||||||
|
"user_id": session_data.get("user_id"),
|
||||||
|
"session_type": session_data.get("session_type"),
|
||||||
|
"status": session_data.get("status"),
|
||||||
|
"current_step": session_data.get("current_step", 1),
|
||||||
|
"created_at": DataFormatter._format_datetime(session_data.get("created_at")),
|
||||||
|
"updated_at": DataFormatter._format_datetime(session_data.get("updated_at")),
|
||||||
|
"expires_at": DataFormatter._format_datetime(session_data.get("expires_at"))
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加可选字段
|
||||||
|
optional_fields = ["title", "description", "session_config", "metadata"]
|
||||||
|
for field in optional_fields:
|
||||||
|
if field in session_data:
|
||||||
|
formatted[field] = session_data[field]
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_step_data(step_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
格式化步骤数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
step_data: 原始步骤数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的步骤数据
|
||||||
|
"""
|
||||||
|
formatted = {
|
||||||
|
"step_id": step_data.get("step_id"),
|
||||||
|
"session_id": step_data.get("session_id"),
|
||||||
|
"step_number": step_data.get("step_number"),
|
||||||
|
"step_type": step_data.get("step_type"),
|
||||||
|
"status": step_data.get("status"),
|
||||||
|
"execution_mode": step_data.get("execution_mode", "sync"),
|
||||||
|
"started_at": DataFormatter._format_datetime(step_data.get("started_at")),
|
||||||
|
"completed_at": DataFormatter._format_datetime(step_data.get("completed_at")),
|
||||||
|
"created_at": DataFormatter._format_datetime(step_data.get("created_at"))
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加执行结果
|
||||||
|
if "result_data" in step_data:
|
||||||
|
formatted["result_data"] = step_data["result_data"]
|
||||||
|
|
||||||
|
# 添加错误信息
|
||||||
|
if "error_message" in step_data:
|
||||||
|
formatted["error_message"] = step_data["error_message"]
|
||||||
|
|
||||||
|
# 计算执行时长
|
||||||
|
if step_data.get("started_at") and step_data.get("completed_at"):
|
||||||
|
start_time = step_data["started_at"]
|
||||||
|
end_time = step_data["completed_at"]
|
||||||
|
if isinstance(start_time, datetime) and isinstance(end_time, datetime):
|
||||||
|
duration = (end_time - start_time).total_seconds()
|
||||||
|
formatted["execution_duration_seconds"] = duration
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_workflow_state_data(state_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
格式化工作流状态数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
state_data: 原始状态数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的状态数据
|
||||||
|
"""
|
||||||
|
formatted = {
|
||||||
|
"state_id": state_data.get("state_id"),
|
||||||
|
"session_id": state_data.get("session_id"),
|
||||||
|
"state_type": state_data.get("state_type"),
|
||||||
|
"status": state_data.get("status"),
|
||||||
|
"priority": state_data.get("priority"),
|
||||||
|
"created_at": DataFormatter._format_datetime(state_data.get("created_at")),
|
||||||
|
"updated_at": DataFormatter._format_datetime(state_data.get("updated_at")),
|
||||||
|
"expires_at": DataFormatter._format_datetime(state_data.get("expires_at"))
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加状态特定数据
|
||||||
|
if "state_data" in state_data:
|
||||||
|
formatted["state_data"] = state_data["state_data"]
|
||||||
|
|
||||||
|
# 添加可选字段
|
||||||
|
optional_fields = ["category", "related_step", "assigned_to", "processed_by", "processed_at"]
|
||||||
|
for field in optional_fields:
|
||||||
|
if field in state_data:
|
||||||
|
if field == "processed_at":
|
||||||
|
formatted[field] = DataFormatter._format_datetime(state_data[field])
|
||||||
|
else:
|
||||||
|
formatted[field] = state_data[field]
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_memory_data(memory_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
格式化记忆数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
memory_data: 原始记忆数据
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的记忆数据
|
||||||
|
"""
|
||||||
|
formatted = {
|
||||||
|
"memory_id": memory_data.get("memory_id"),
|
||||||
|
"user_id": memory_data.get("user_id"),
|
||||||
|
"memory_type": memory_data.get("memory_type"),
|
||||||
|
"memory_key": memory_data.get("memory_key"),
|
||||||
|
"category": memory_data.get("category"),
|
||||||
|
"access_level": memory_data.get("access_level"),
|
||||||
|
"importance": memory_data.get("importance"),
|
||||||
|
"weight": memory_data.get("weight"),
|
||||||
|
"access_count": memory_data.get("access_count", 0),
|
||||||
|
"created_at": DataFormatter._format_datetime(memory_data.get("created_at")),
|
||||||
|
"updated_at": DataFormatter._format_datetime(memory_data.get("updated_at"))
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加记忆数据(可能需要脱敏)
|
||||||
|
if "memory_data" in memory_data:
|
||||||
|
formatted["memory_data"] = DataFormatter._sanitize_memory_data(
|
||||||
|
memory_data["memory_data"],
|
||||||
|
memory_data.get("sensitivity", "low")
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加可选字段
|
||||||
|
optional_fields = ["description", "tags", "last_accessed", "expires_at"]
|
||||||
|
for field in optional_fields:
|
||||||
|
if field in memory_data:
|
||||||
|
if field in ["last_accessed", "expires_at"]:
|
||||||
|
formatted[field] = DataFormatter._format_datetime(memory_data[field])
|
||||||
|
else:
|
||||||
|
formatted[field] = memory_data[field]
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_list_response(items: List[Dict[str, Any]],
|
||||||
|
formatter_func,
|
||||||
|
total: int = 0,
|
||||||
|
page: int = 0,
|
||||||
|
page_size: int = 0) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
格式化列表响应
|
||||||
|
|
||||||
|
Args:
|
||||||
|
items: 数据项列表
|
||||||
|
formatter_func: 格式化函数
|
||||||
|
total: 总数量
|
||||||
|
page: 页码
|
||||||
|
page_size: 页大小
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 格式化的列表响应
|
||||||
|
"""
|
||||||
|
formatted_items = [formatter_func(item) for item in items]
|
||||||
|
|
||||||
|
result:Any = {"items": formatted_items}
|
||||||
|
|
||||||
|
if total > 0:
|
||||||
|
result["total"] = total
|
||||||
|
|
||||||
|
if page > 0 and page_size > 0:
|
||||||
|
result["pagination"] = {
|
||||||
|
"current_page": page,
|
||||||
|
"page_size": page_size,
|
||||||
|
"total_pages": (total + page_size - 1) // page_size if total else 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_datetime(dt: Union[datetime, str, None]) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
格式化日期时间
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dt: 日期时间对象或字符串
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[str]: 格式化的日期时间字符串
|
||||||
|
"""
|
||||||
|
if dt is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if isinstance(dt, datetime):
|
||||||
|
return dt.isoformat()
|
||||||
|
|
||||||
|
if isinstance(dt, str):
|
||||||
|
return dt
|
||||||
|
|
||||||
|
return str(dt)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _sanitize_memory_data(memory_data: Dict[str, Any], sensitivity: str) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
根据敏感度脱敏记忆数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
memory_data: 记忆数据
|
||||||
|
sensitivity: 敏感度级别
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict: 脱敏后的数据
|
||||||
|
"""
|
||||||
|
if sensitivity in ["high", "confidential"]:
|
||||||
|
# 高敏感度数据需要脱敏
|
||||||
|
sanitized = {}
|
||||||
|
for key, value in memory_data.items():
|
||||||
|
if key in ["password", "token", "secret", "private_key"]:
|
||||||
|
sanitized[key] = "***"
|
||||||
|
elif isinstance(value, str) and len(value) > 10:
|
||||||
|
# 长字符串部分脱敏
|
||||||
|
sanitized[key] = value[:3] + "***" + value[-3:]
|
||||||
|
else:
|
||||||
|
sanitized[key] = value
|
||||||
|
return sanitized
|
||||||
|
|
||||||
|
return memory_data.copy()
|
||||||
|
|
||||||
|
class DateTimeEncoder(json.JSONEncoder):
|
||||||
|
"""自定义JSON编码器,处理日期时间对象"""
|
||||||
|
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj, datetime):
|
||||||
|
return obj.isoformat()
|
||||||
|
elif isinstance(obj, date):
|
||||||
|
return obj.isoformat()
|
||||||
|
elif isinstance(obj, Decimal):
|
||||||
|
return float(obj)
|
||||||
|
return super().default(obj)
|
||||||
|
|
||||||
|
def format_file_size(size_bytes: int) -> str:
|
||||||
|
"""
|
||||||
|
格式化文件大小
|
||||||
|
|
||||||
|
Args:
|
||||||
|
size_bytes: 字节数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 格式化的文件大小
|
||||||
|
"""
|
||||||
|
if size_bytes == 0:
|
||||||
|
return "0 B"
|
||||||
|
|
||||||
|
size_names = ["B", "KB", "MB", "GB", "TB"]
|
||||||
|
import math
|
||||||
|
i = int(math.floor(math.log(size_bytes, 1024)))
|
||||||
|
p = math.pow(1024, i)
|
||||||
|
s = round(size_bytes / p, 2)
|
||||||
|
return f"{s} {size_names[i]}"
|
||||||
|
|
||||||
|
def format_duration(seconds: float) -> str:
|
||||||
|
"""
|
||||||
|
格式化持续时间
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seconds: 秒数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 格式化的持续时间
|
||||||
|
"""
|
||||||
|
if seconds < 60:
|
||||||
|
return f"{seconds:.2f}秒"
|
||||||
|
elif seconds < 3600:
|
||||||
|
minutes = seconds / 60
|
||||||
|
return f"{minutes:.2f}分钟"
|
||||||
|
else:
|
||||||
|
hours = seconds / 3600
|
||||||
|
return f"{hours:.2f}小时"
|
||||||
|
|
||||||
|
def truncate_text(text: str, max_length: int = 100, suffix: str = "...") -> str:
|
||||||
|
"""
|
||||||
|
截断文本
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: 原始文本
|
||||||
|
max_length: 最大长度
|
||||||
|
suffix: 后缀
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 截断后的文本
|
||||||
|
"""
|
||||||
|
if len(text) <= max_length:
|
||||||
|
return text
|
||||||
|
|
||||||
|
return text[:max_length - len(suffix)] + suffix
|
||||||
|
|
||||||
|
def mask_sensitive_info(text: str, mask_char: str = "*", visible_chars: int = 3) -> str:
|
||||||
|
"""
|
||||||
|
遮蔽敏感信息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: 原始文本
|
||||||
|
mask_char: 遮蔽字符
|
||||||
|
visible_chars: 可见字符数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 遮蔽后的文本
|
||||||
|
"""
|
||||||
|
if len(text) <= visible_chars * 2:
|
||||||
|
return mask_char * len(text)
|
||||||
|
|
||||||
|
return text[:visible_chars] + mask_char * (len(text) - visible_chars * 2) + text[-visible_chars:]
|
||||||
58
utils/logger.py
Normal file
58
utils/logger.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
|
def get_logger(name):
|
||||||
|
"""
|
||||||
|
获取统一配置的logger实例
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: logger名称,通常使用 __name__
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
logging.Logger: 配置好的logger实例
|
||||||
|
"""
|
||||||
|
logger = logging.getLogger(name)
|
||||||
|
|
||||||
|
# 如果logger已经配置过,直接返回
|
||||||
|
if logger.handlers:
|
||||||
|
return logger
|
||||||
|
|
||||||
|
# 设置日志级别
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
# 创建日志目录
|
||||||
|
log_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'logs')
|
||||||
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# 创建日志格式
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||||
|
datefmt='%Y-%m-%d %H:%M:%S'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 文件处理器 - 使用轮转日志
|
||||||
|
log_file = os.path.join(log_dir, 'langgraph.log')
|
||||||
|
file_handler = RotatingFileHandler(
|
||||||
|
log_file,
|
||||||
|
maxBytes=10*1024*1024, # 10MB
|
||||||
|
backupCount=5,
|
||||||
|
encoding='utf-8'
|
||||||
|
)
|
||||||
|
file_handler.setLevel(logging.INFO)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 控制台处理器
|
||||||
|
console_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
console_handler.setLevel(logging.INFO)
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 添加处理器
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
# 防止日志向上传播
|
||||||
|
logger.propagate = False
|
||||||
|
|
||||||
|
return logger
|
||||||
Loading…
x
Reference in New Issue
Block a user