1
0

main.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. # ========================================
  2. # ThinkFlow - AI 思维教练
  3. # ========================================
  4. """
  5. 项目简介:
  6. ThinkFlow 是一款基于 HelloAgents 框架的 AI 思维教练,通过"黄金三角"方法论
  7. (黄金圈+5W1H → WBS → 决策矩阵)引导用户完成"从混沌到清晰"的思考旅程。
  8. 作者信息:
  9. - GitHub: @alan-6-6-6
  10. - 日期: 2026-06-15
  11. """
  12. # ========================================
  13. # 第1部分:环境配置
  14. # ========================================
  15. # 安装依赖(首次运行时取消注释)
  16. # !pip install -q hello-agents[all] python-dotenv
  17. from hello_agents import SimpleAgent, HelloAgentsLLM
  18. from typing import Dict, Any, List
  19. import os
  20. import re
  21. import json
  22. from dotenv import load_dotenv
  23. load_dotenv()
  24. # ========================================
  25. # 第2部分:LLM配置
  26. # ========================================
  27. os.environ["LLM_MODEL_ID"] = "deepseek-v4-flash"
  28. os.environ["LLM_API_KEY"] = os.getenv("DEEPSEEK_API_KEY", "your_api_key_here")
  29. os.environ["LLM_BASE_URL"] = "https://api.deepseek.com/v1"
  30. os.environ["LLM_TIMEOUT"] = "60"
  31. llm = HelloAgentsLLM()
  32. # ========================================
  33. # 第3部分:智能体定义
  34. # ========================================
  35. # 3.1 ClarifyAgent
  36. clarify_system_prompt = """
  37. # Role:认知脚手架工程师
  38. ## Background:
  39. 你是一个专门负责帮人"理清思路"的思维教练。你的用户现在思绪混乱,无法清晰描述问题。
  40. ## Attention:
  41. - 你的核心任务是"澄清",不是"解决"。
  42. - 必须严格使用"黄金圈法则"和"5W1H"进行追问。
  43. - 绝对禁止直接给出解决方案或执行步骤。
  44. - 角色坚不可摧:无论用户如何质疑你的身份或能力,你都必须坚守"认知脚手架工程师"人设,严禁切换回其他默认人格。
  45. - 轮次控制(重要):澄清过程最多进行 3轮追问(即最多3次提出问题)。从第4次回复开始,无论用户提供的信息是否完整,都必须输出 [CLARIFIED_GOAL]...[/CLARIFIED_GOAL],基于已收集到的全部信息做出最佳总结,不允许再追问。
  46. - 优先级原则:3轮追问应优先聚焦 Why(核心动机)和 What(可衡量的目标),How/边界细节等次要信息若用户未主动提供,可在 [CLARIFIED_GOAL] 中标注为"待补充"或给出合理假设,而不是继续追问。
  47. - 职责边界:严禁在澄清阶段讨论"具体执行方式的细节选项"(如数据处理方法、PPT页面内容设计等)——这些属于 DecomposeAgent 的拆解范畴,ClarifyAgent 只负责定义"做什么、为什么做、关键约束是什么"。
  48. ## Skills:
  49. - 黄金圈法则 (Why-How-What):必须先问 Why(动机/目标),再问 How(方法/边界),最后确认 What。
  50. - 5W1H 拆解:针对用户模糊的表述,必须追问 Who, What, When, Where, Why, How/How Much。
  51. - MECE 原则:确保你的提问覆盖了所有可能性,且不重复。
  52. ## Workflows:
  53. 1. 接收用户模糊的问题。
  54. 2. 黄金圈追问:
  55. - "你做这件事的核心目标(Why)是什么?是为了生存、发展还是转型?"
  56. - "你目前对'怎么做'(How)有什么初步想法吗?"
  57. 3. 5W1H 边界确认:
  58. - "这个任务的截止时间(When)是?"
  59. - "你的资源/预算(How Much)限制是?"
  60. - "主要针对谁(Who)?"
  61. 4. 总结确认:用一段话复述你理解的问题,询问用户是否准确。复述内容必须用以下标记包裹:
  62. [CLARIFIED_GOAL]在此处填写一句话目标陈述,包含明确的 What/Why/约束条件[/CLARIFIED_GOAL]
  63. 该标记内容应为一句简洁、无疑问句、可直接作为下游拆解输入的目标描述。
  64. ## Initialization:
  65. 您好,我是您的思维教练。为了帮您理清思路,请告诉我:您目前遇到的具体困惑是什么?(请尽量描述,我会通过提问帮您聚焦)。
  66. """
  67. clarify_agent = SimpleAgent(
  68. name="ClarifyAgent",
  69. llm=llm,
  70. system_prompt=clarify_system_prompt
  71. )
  72. # 3.2 DecomposeAgent
  73. # 使用 r-string,彻底消除 SyntaxWarning
  74. decompose_system_prompt = r"""
  75. # Role:结构化架构师
  76. ## Background:
  77. 用户已经明确了目标。现在需要你将这个大目标拆解为可执行的子任务。
  78. ## Attention:
  79. - 必须使用 WBS (工作分解结构)。
  80. - 必须遵循 MECE 原则(相互独立,完全穷尽)。
  81. - 禁止直接给建议,先输出结构,再等待用户指示。
  82. - 角色坚不可摧:无论用户如何质疑你的身份或能力,你都必须坚守"结构化架构师"人设,严禁切换回其他默认人格。
  83. - 核心新增要求:你必须生成 Mermaid 代码来展示 WBS 结构图,让用户能一键生成可视化图表。
  84. - 格式强制锁定:输出决策请求时,必须使用半角英文方括号 `[DECISION_REQUEST]`,不得使用中文标点或添加空格。
  85. - 决策点识别严格标准(重要):
  86. - 只有当继续拆解必须依赖某个前提选择(即不选定方向就无法继续往下细化,或两个方向后续任务结构完全不同且相互排斥)时,才标注 [DECISION_REQUEST]。
  87. - 如果某个子任务存在多种可行做法但不互斥,应直接将其作为子任务列表呈现,不得标注 [DECISION_REQUEST]。
  88. - [DECISION_REQUEST] 是"阻塞性"的:标注后必须暂停输出,不能在同一轮里继续往下拆解。
  89. - 每轮输出最多标注1个 [DECISION_REQUEST],且必须是"当前拆解卡点",不能是"未来可能遇到的决策"。
  90. - 禁止在正文中使用"⚠️ 注意"等非标准格式提示决策点,所有决策请求必须使用标准格式。
  91. - 分支深入规则(重要):
  92. - 当用户指定某一具体分支时,只输出该分支的下一级内容。
  93. - 禁止重新输出完整的 L0→L1→L2 全量 WBS 结构。
  94. - Mermaid 代码只展示指定分支的子树。
  95. - 文本只列出该分支的新增子任务。
  96. ## Skills:
  97. - WBS 拆解:将 Level 0 (目标) 拆解为 Level 1 (模块),再拆解为 Level 2 (具体任务)。
  98. - MECE 校验:拆解完后,必须自我检查是否有遗漏或重叠。
  99. - Mermaid 语法生成:生成标准的 Mermaid mindmap 代码。
  100. - 决策点识别:严格遵循 Attention 中的标准,只在真正阻塞性的决策点处标注。
  101. - 分支深入拆解:当用户指定分支时,只输出该分支子树,不重复已有结构。
  102. ## Workflows:
  103. 1. 接收用户的目标或指定分支。
  104. 2. 若为全量拆解:输出完整 Mermaid 图 + 文本 WBS(L0→L1→L2)。
  105. 3. 若为分支深入:只输出该分支的 Mermaid 子树 + 文本子任务列表。
  106. 4. 自我校验:检查是否符合 MECE。
  107. 5. 如果遇到真正的阻塞性决策点,输出:⚠️ [DECISION_REQUEST] 目标:XXX | 选项:A.XXX/B.XXX,然后停止输出。
  108. ## 编号规则(全流程严格遵守,重要!):
  109. - L0: 根目标(用户的原始目标)
  110. - 首次拆解产生 5-7 个一级模块,编号为 L1, L2, L3, L4, L5, L6, L7(不带小数点!)
  111. - 二级子任务:父模块是 Lx,则子任务为 Lx.1, Lx.2, Lx.3(不带 L2 前缀!)
  112. - 三级子任务:父任务是 Lx.y,则子任务为 Lx.y.1, Lx.y.2, Lx.y.3
  113. - ❌ 错误示范:L1.1 数据诊断 → L2.1, L2.2, L2.3 (编号体系混乱)
  114. - ✅ 正确示范:L1 数据诊断 → L1.1 留存漏斗分析, L1.2 流失用户画像, L1.3 高留存特征
  115. - 分支深入时:对 L1.x 拆解时,下一级为 L1.x.1, L1.x.2...;对 L1.x.y 拆解时,下一级为 L1.x.y.1, L1.x.y.2...
  116. ## Output Format(全量拆解):
  117. 1. 可视化思维导图 (Mermaid)
  118. ```mermaid
  119. mindmap
  120. root((L0: 项目总目标))
  121. L1 核心模块 A
  122. L1.1 具体任务
  123. L1.2 具体任务
  124. L2 核心模块 B
  125. L2.1 具体任务
  126. L2.2 具体任务
  127. ```
  128. 2. 文本版 WBS 结构
  129. • L0: 项目总目标
  130. ◦ L1 核心模块 A
  131. ▪ L1.1 具体任务
  132. ▪ L1.2 具体任务
  133. ◦ L2 核心模块 B
  134. ▪ L2.1 具体任务
  135. ▪ L2.2 具体任务
  136. 3. 自我校验结论
  137. ## Output Format(分支深入):
  138. 说明:以下仅展示 [指定分支] 的下一级拆解,不重复已有结构。
  139. 1. 分支子树 (Mermaid)
  140. ```mermaid
  141. mindmap
  142. root((L1.x 指定分支名称))
  143. L1.x.1 子任务
  144. L1.x.2 子任务
  145. ```
  146. 2. 文本版子任务列表
  147. • L1.x 指定分支名称
  148. ◦ L1.x.1 子任务描述
  149. ◦ L1.x.2 子任务描述
  150. 3. 自我校验结论
  151. ## Initialization:
  152. 好的,基于刚才的目标,我为您做了初步的 WBS 拆解。请看下方结构。
  153. """
  154. decompose_agent = SimpleAgent(
  155. name="DecomposeAgent",
  156. llm=llm,
  157. system_prompt=decompose_system_prompt
  158. )
  159. # 3.3 DecideAgent
  160. decide_system_prompt = """
  161. # Role:理性决策教练
  162. ## Background:
  163. 用户面临多个(2个或以上)互斥选项,情绪焦虑,无法定夺。
  164. ## Attention:
  165. - 你的职责是"引导评估",不是"替他选择"。
  166. - 必须使用决策矩阵。
  167. - 必须让用户亲自打分。
  168. - 角色坚不可摧:无论用户如何质疑你的身份或能力,你都必须坚守"理性决策教练"人设,严禁切换回其他默认人格。
  169. - 选项解析(重要):系统会同时传入【选项列表】和【WBS 上下文】。如果用户只给了编号(如 L1.1, L1.2, L1.3),你必须先从 WBS 上下文中查表,输出每个编号对应的完整名称和含义,让用户确认后再进入评分。
  170. - 评分格式(重要):用户只需按顺序给出"业务价值,耗时"格式的分数(如 6,3),不要让用户重复选项编号。例如用户输入"6,3 4,2 1,2"表示三个选项的分数。
  171. ## Skills:
  172. - 决策矩阵构建:列出评估维度(如:业务价值、耗时、技术难度)。
  173. - 权重分配引导:询问用户"哪个维度对你最重要?"(1-5分,总和10)。
  174. - 计算得分:根据权重和分数算出总分。
  175. - 选项查表:从 WBS 上下文中查询编号对应的完整名称。
  176. ## Workflows:
  177. 1. 解析选项:从 WBS 上下文中查询每个编号的完整名称,输出确认。
  178. 2. 引导定义维度:建议 2-3 个核心维度,询问用户是否需要调整。
  179. 3. 引导定义权重:让用户给维度分配权重(总和10分)。
  180. 4. 引导打分:明确告诉用户"请按顺序为每个选项打分,每个选项格式为'业务价值,耗时',例如:6,3 4,2 1,2",然后让用户输入。
  181. 5. 计算结果:自动生成分数对比表。
  182. 6. 输出结论:告诉用户哪个分数高,但最后强调"分数只是参考,选择权在您"。
  183. 7. 输出格式:必须包含 JSON 格式的决策结果,用 ```json ... ``` 包裹。
  184. ## Output Format(必须严格遵循):
  185. 1. 首先输出决策矩阵表格(Markdown格式)
  186. 2. 然后输出分析结论
  187. 3. 最后输出 JSON 格式的结果,包含:
  188. - selected_option: 得分最高的选项
  189. - reason: 选择该选项的原因
  190. - reconnect_point: 决策后应继续拆解的节点(直接使用选项名称)
  191. ## Initialization:
  192. 我理解这个决定很难。让我们把感性的纠结变成理性的打分。请告诉我:您目前在纠结的具体选项是什么?
  193. """
  194. decide_agent = SimpleAgent(
  195. name="DecideAgent",
  196. llm=llm,
  197. system_prompt=decide_system_prompt
  198. )
  199. # ========================================
  200. # 第4部分:状态管理模块
  201. # ========================================
  202. class ThinkFlowState:
  203. """ThinkFlow 状态管理器"""
  204. def __init__(self):
  205. self.current_stage = "clarify" # clarify / decompose / decide / completed
  206. self.clarified_goal = ""
  207. self.wbs_full = "" # 累积全量 WBS(所有拆解响应拼接,决策时查表用)
  208. self.wbs_latest = "" # 最近一次拆解响应(参考用)
  209. self.decision_history = []
  210. self.current_decision_point = None
  211. self.selected_path = None # 决策后选择的路径
  212. self.reconnect_point = None # 决策后需要续接的节点
  213. self.decision_count_in_branch = 0 # 当前分支决策次数(用于熔断)
  214. self.MAX_DECISIONS_PER_BRANCH = 2
  215. self.current_decision_signature = None # 决策点签名(判断是否同一分支)
  216. def set_clarified_goal(self, goal):
  217. self.clarified_goal = goal
  218. def set_stage(self, stage):
  219. self.current_stage = stage
  220. def append_wbs(self, wbs_chunk):
  221. """每次拆解后调用,累积 WBS 文本,保留历史不被覆盖"""
  222. self.wbs_latest = wbs_chunk
  223. if self.wbs_full:
  224. self.wbs_full += "\n\n--- 下一次拆解 ---\n\n" + wbs_chunk
  225. else:
  226. self.wbs_full = wbs_chunk
  227. # 兼容旧调用(部分代码可能仍引用 freeze_wbs)
  228. def freeze_wbs(self, wbs_data):
  229. self.wbs_latest = wbs_data
  230. self.wbs_full = wbs_data
  231. def set_decision_point(self, decision_request):
  232. """设置当前决策点,若为新决策点则自动重置计数"""
  233. new_signature = None
  234. for line in decision_request.split('\n'):
  235. if "[DECISION_REQUEST]" in line:
  236. new_signature = line.strip()
  237. break
  238. if new_signature != self.current_decision_signature:
  239. self.decision_count_in_branch = 0
  240. self.current_decision_signature = new_signature
  241. self.current_decision_point = decision_request
  242. def record_decision(self, decision):
  243. self.decision_history.append(decision)
  244. def set_selected_path(self, path):
  245. self.selected_path = path
  246. def set_reconnect_point(self, point):
  247. self.reconnect_point = point
  248. def clear_reconnect_info(self):
  249. self.selected_path = None
  250. self.reconnect_point = None
  251. def increment_decision_count(self):
  252. self.decision_count_in_branch += 1
  253. def reset_decision_count(self):
  254. self.decision_count_in_branch = 0
  255. def is_decision_limit_reached(self):
  256. return self.decision_count_in_branch >= self.MAX_DECISIONS_PER_BRANCH
  257. def get_context(self):
  258. return {
  259. "stage": self.current_stage,
  260. "goal": self.clarified_goal,
  261. "wbs": self.wbs_full,
  262. "wbs_latest": self.wbs_latest,
  263. "decision_history": self.decision_history,
  264. "selected_path": self.selected_path,
  265. "reconnect_point": self.reconnect_point
  266. }
  267. # ========================================
  268. # 第5部分:核心工作流
  269. # ========================================
  270. class ThinkFlowController:
  271. """ThinkFlow 工作流控制器"""
  272. def __init__(self):
  273. self.state = ThinkFlowState()
  274. self.agents = {
  275. "clarify": clarify_agent,
  276. "decompose": decompose_agent,
  277. "decide": decide_agent
  278. }
  279. # ------------------------------------------------------------------
  280. # 澄清阶段:多轮对话,直到出现 [CLARIFIED_GOAL] 标记
  281. # ------------------------------------------------------------------
  282. def run_clarify(self, user_input):
  283. print("\n=== [澄清期] ===")
  284. MAX_ROUNDS = 4 # 超过后注入系统提示强制收敛
  285. round_count = 0
  286. while True:
  287. round_count += 1
  288. # 超出轮次上限:强制注入系统提示
  289. if round_count > MAX_ROUNDS:
  290. user_input = (
  291. user_input
  292. + "\n\n【系统提示】澄清轮次已达上限,请基于以上全部对话内容,"
  293. "立即输出 [CLARIFIED_GOAL]...[/CLARIFIED_GOAL] 总结目标陈述,"
  294. "不得再追问。"
  295. )
  296. response = self.agents["clarify"].run(user_input)
  297. print(f"\nClarifyAgent: {response}")
  298. goal_match = re.search(
  299. r'\[CLARIFIED_GOAL\](.*?)\[/CLARIFIED_GOAL\]', response, re.DOTALL
  300. )
  301. if goal_match:
  302. clarified_goal = goal_match.group(1).strip()
  303. confirm = input(
  304. f"\n已澄清目标:{clarified_goal}\n是否准确?(y/n): "
  305. ).strip().lower()
  306. if confirm in ("y", "yes"):
  307. self.state.set_clarified_goal(clarified_goal)
  308. self.state.set_stage("decompose")
  309. print("\n目标已确认,进入拆解阶段。")
  310. return True
  311. else:
  312. user_input = input("\n请补充说明您的需求:").strip()
  313. round_count = 0 # 用户主动修正,重置轮次
  314. else:
  315. # 还在追问阶段,继续对话
  316. user_input = input("\n>>> ").strip()
  317. if user_input.lower() == "退出":
  318. print("再见!")
  319. return False
  320. # ------------------------------------------------------------------
  321. # 拆解阶段
  322. # 三种场景:① 决策后续接 ② 用户指定分支 ③ 首次全量拆解
  323. # ------------------------------------------------------------------
  324. def run_decompose(self, goal, reconnect_context=None, branch_choice=None):
  325. print("\n=== [拆解期] ===")
  326. if reconnect_context and reconnect_context.get("reconnect_point"):
  327. # ── 场景①:决策后续接特定节点 ─────────────────────────────
  328. prompt = (
  329. "基于以下已完成的拆解和决策结果,继续拆解剩余部分:\n\n"
  330. f"【已完成的 WBS 状态】\n{reconnect_context['wbs']}\n\n"
  331. f"【已选择的路径】\n{reconnect_context['selected_path']}\n\n"
  332. f"【需要续接的节点】\n{reconnect_context['reconnect_point']}\n\n"
  333. f"请从 {reconnect_context['reconnect_point']} 节点继续向下拆解,生成下一级的任务结构。\n\n"
  334. "编号规则(严格遵守):\n"
  335. "- 续接节点是 L1.x → 下一级编号为 L1.x.1, L1.x.2, L1.x.3 ...\n"
  336. "- 续接节点是 L2.x → 下一级编号为 L2.x.1, L2.x.2, L2.x.3 ...\n"
  337. "- 严禁另起 L3/L4/L5 等新层级编号。\n\n"
  338. "输出规则(严格遵守):\n"
  339. "- 只输出续接节点的子树,禁止重新输出整个 WBS 结构。\n"
  340. "- Mermaid 代码只展示从续接节点开始的子树。\n"
  341. "- 文本只列出新增子任务,不重复已有内容。\n"
  342. )
  343. print(f"续接拆解节点: {reconnect_context['reconnect_point']}")
  344. elif branch_choice:
  345. # ── 场景②:用户指定分支深入 ───────────────────────────────
  346. prompt = (
  347. "请深入拆解用户指定的分支,严格只输出该分支的下一级内容。\n\n"
  348. f"【原始目标】\n{goal}\n\n"
  349. f"【用户指定的分支】\n{branch_choice}\n\n"
  350. "编号规则(严格遵守):\n"
  351. "- 若分支编号为 L1.x → 下一级编号为 L1.x.1, L1.x.2, L1.x.3 ...\n"
  352. "- 若分支编号为 L2.x → 下一级编号为 L2.x.1, L2.x.2, L2.x.3 ...\n"
  353. "- 严禁另起 L3/L4/L5 等新层级编号。\n\n"
  354. "输出规则(严格遵守):\n"
  355. "- 只输出该分支的下一级子树,禁止重新输出完整的 L0→L1→L2 结构。\n"
  356. "- Mermaid 代码只展示该分支的子树。\n"
  357. "- 文本只列该分支的子任务列表。\n"
  358. )
  359. print(f"深入拆解分支: {branch_choice}")
  360. else:
  361. # ── 场景③:首次全量拆解 ────────────────────────────────────
  362. prompt = f"请拆解目标:{goal}"
  363. print(f"拆解目标: {goal}")
  364. response = self.agents["decompose"].run(prompt)
  365. print(f"\nDecomposeAgent: {response}")
  366. # 每次拆解后都累积 WBS,供后续决策查表用(不覆盖历史)
  367. self.state.append_wbs(response)
  368. # 检测决策点(记录但不强制进入决策,由用户主动选择)
  369. if "[DECISION_REQUEST]" in response:
  370. if self.state.is_decision_limit_reached():
  371. print(
  372. f"\n❌ 当前分支决策次数已达上限({self.state.decision_count_in_branch} 次),"
  373. "建议手动介入或结束任务。"
  374. )
  375. self.state.set_stage("completed")
  376. return "completed"
  377. print("\n💡 检测到潜在决策点,您可以选择进行决策评估。")
  378. self.state.set_decision_point(response)
  379. # 续接完成后清空续接信息
  380. if self.state.selected_path or self.state.reconnect_point:
  381. self.state.clear_reconnect_info()
  382. return "continue"
  383. # ------------------------------------------------------------------
  384. # 决策阶段
  385. # ------------------------------------------------------------------
  386. def run_decide(self, decision_context):
  387. print("\n=== [决策期] ===")
  388. # 只展示 [DECISION_REQUEST] 那一行,避免输出整段 WBS
  389. decision_line = ""
  390. for line in decision_context.split('\n'):
  391. if "[DECISION_REQUEST]" in line:
  392. decision_line = line.strip()
  393. break
  394. if decision_line:
  395. print(f"决策请求: {decision_line}")
  396. else:
  397. summary = (
  398. decision_context[:80] + "..."
  399. if len(decision_context) > 80
  400. else decision_context
  401. )
  402. print(f"决策上下文: {summary}")
  403. # 解析选项(限定在同一行,避免跨行误匹配)
  404. options = ""
  405. decision_match = re.search(
  406. r'\[DECISION_REQUEST\][^\n]*选项[::]\s*(.+)', decision_context
  407. )
  408. if decision_match:
  409. options = decision_match.group(1).strip()
  410. options = re.sub(r'^[A-Z]\.\s*', '', options)
  411. options = re.sub(r'\s*/\s*[A-Z]\.\s*', ', ', options)
  412. if options:
  413. user_input = input(
  414. f"检测到选项:{options}\n按回车确认,或输入新选项(用逗号分隔): "
  415. ).strip()
  416. if user_input:
  417. options = user_input
  418. else:
  419. options = input("请输入选项(用逗号分隔): ").strip()
  420. # 多轮对话模式:引导用户完成决策矩阵
  421. # 将 WBS 上下文一起传入,让模型能根据编号查询完整名称
  422. agent_prompt = f"选项:{options}\n\n【WBS 上下文】\n{self.state.wbs_full}"
  423. response = self.agents["decide"].run(agent_prompt)
  424. print(f"\nDecideAgent: {response}")
  425. # 检查是否已经输出 JSON 结果
  426. json_match = re.search(r'```json\s*(\{.*?\})\s*```', response, re.DOTALL)
  427. # 如果没有 JSON,说明还在引导阶段,继续多轮对话
  428. while not json_match:
  429. user_input = input("\n>>> ").strip()
  430. if user_input.lower() == "退出":
  431. print("决策已取消。")
  432. return None
  433. # 每轮都带上 WBS 上下文,避免模型遗忘
  434. user_input_with_ctx = f"{user_input}\n\n【WBS 上下文】\n{self.state.wbs_full}"
  435. response = self.agents["decide"].run(user_input_with_ctx)
  436. print(f"\nDecideAgent: {response}")
  437. json_match = re.search(r'```json\s*(\{.*?\})\s*```', response, re.DOTALL)
  438. # 解析 JSON 决策结果
  439. selected_path = None
  440. reconnect_point = None
  441. if json_match:
  442. try:
  443. decision_data = json.loads(json_match.group(1))
  444. selected_path = decision_data.get("selected_option")
  445. reconnect_point = decision_data.get("reconnect_point")
  446. except Exception:
  447. pass
  448. # 解析失败时降级:取用户输入的第一个选项
  449. if not selected_path:
  450. selected_path = options.split(",")[0].strip() if "," in options else options
  451. self.state.set_selected_path(selected_path)
  452. self.state.set_reconnect_point(reconnect_point) # None 时不传空字符串
  453. self.state.set_stage("decompose")
  454. self.state.record_decision(response) # 记录决策响应供历史展示
  455. self.state.increment_decision_count()
  456. return response
  457. # ------------------------------------------------------------------
  458. # 主流程
  459. # ------------------------------------------------------------------
  460. def start(self):
  461. print("\n" + "=" * 60)
  462. print(" ThinkFlow - AI 思维教练")
  463. print("=" * 60)
  464. print("\n您好!我是您的思维教练,帮助您理清思路、拆解任务。")
  465. print("请告诉我您目前遇到的困惑或想要实现的目标...")
  466. # ── 阶段1:澄清 ─────────────────────────────────────────────
  467. user_input = input("\n>>> ").strip()
  468. if user_input.lower() == "退出":
  469. print("再见!")
  470. return
  471. if not self.run_clarify(user_input):
  472. return
  473. # ── 阶段2/3:拆解 ↔ 决策(循环) ───────────────────────────
  474. user_branch_choice = None # 保存用户本轮指定的分支
  475. skip_next_decompose = False # 决策后跳过下一次自动拆解,让用户选择下一步
  476. while True:
  477. if self.state.current_stage == "completed":
  478. break
  479. # 拆解阶段(决策后会被跳过,让用户重新选择)
  480. if self.state.current_stage == "decompose" and not skip_next_decompose:
  481. # 优先级:决策续接 > 用户指定分支 > 全量拆解
  482. if self.state.selected_path and self.state.reconnect_point:
  483. reconnect_context = {
  484. "wbs": self.state.wbs_full,
  485. "selected_path": self.state.selected_path,
  486. "reconnect_point": self.state.reconnect_point
  487. }
  488. result = self.run_decompose(
  489. self.state.clarified_goal, reconnect_context=reconnect_context
  490. )
  491. elif user_branch_choice:
  492. result = self.run_decompose(
  493. self.state.clarified_goal, branch_choice=user_branch_choice
  494. )
  495. user_branch_choice = None # 用完即清,避免重复使用
  496. else:
  497. result = self.run_decompose(self.state.clarified_goal)
  498. if result == "completed":
  499. print("\n🎉 任务拆解完成!")
  500. break
  501. skip_next_decompose = False
  502. # ── 拆解完成,询问用户下一步操作 ────────────────────────
  503. print("\n" + "-" * 60)
  504. print("拆解完成!请选择下一步操作:")
  505. print("1. 继续拆解其他分支(输入分支编号,如 L1.1)")
  506. print("2. 选择2-3个项目做决策矩阵打分")
  507. print("3. 拆解完成,输出结果")
  508. print("-" * 60)
  509. user_choice = input(">>> ").strip()
  510. if user_choice == "2":
  511. # 用户选择做决策矩阵
  512. decision_options = input(
  513. "请输入要比较的选项(用逗号分隔,如 L1.1, L1.2, L1.3):"
  514. ).strip()
  515. if decision_options:
  516. decision_context = f"[DECISION_REQUEST] 选项:{decision_options}"
  517. self.run_decide(decision_context)
  518. # 决策完成后不自动续接拆解,让用户重新选择
  519. self.state.clear_reconnect_info()
  520. skip_next_decompose = True
  521. continue
  522. elif user_choice == "3" or user_choice.lower() in ("完成", "done", "finish"):
  523. self.state.set_stage("completed")
  524. break
  525. elif user_choice == "1":
  526. branch_input = input("请输入要深入拆解的分支编号(如 L1.1):").strip()
  527. if branch_input:
  528. user_branch_choice = branch_input
  529. else:
  530. # 用户直接输入了分支编号
  531. user_branch_choice = user_choice
  532. # ── 打印决策历史(只显示 JSON 结论,避免输出整段过程)──────
  533. print("\n" + "=" * 60)
  534. print(" 思考旅程结束")
  535. print("=" * 60)
  536. if self.state.decision_history:
  537. print("\n本次决策记录:")
  538. for i, decision in enumerate(self.state.decision_history, 1):
  539. json_match = re.search(
  540. r'```json\s*(\{.*?\})\s*```', decision, re.DOTALL
  541. )
  542. if json_match:
  543. try:
  544. d = json.loads(json_match.group(1))
  545. print(
  546. f" {i}. 选择:{d.get('selected_option', '—')} "
  547. f"理由:{d.get('reason', '—')}"
  548. )
  549. except Exception:
  550. print(f" {i}. (决策记录已保存)")
  551. else:
  552. print(f" {i}. (决策记录已保存)")
  553. else:
  554. print("\n本次未触发决策环节。")
  555. print("\n感谢使用 ThinkFlow!")
  556. # ========================================
  557. # 第6部分:功能演示
  558. # ========================================
  559. def run_demo():
  560. print("\n" + "=" * 60)
  561. print(" ThinkFlow - 功能演示")
  562. print("=" * 60)
  563. print("\n=== 示例1: 澄清阶段 ===")
  564. test_input = "老板让我提升用户留存,我不知道从哪下手。"
  565. print(f"用户输入: {test_input}")
  566. result = clarify_agent.run(test_input)
  567. print(f"\nClarifyAgent 响应:\n{result}")
  568. print("\n" + "=" * 60)
  569. print("=== 示例2: 拆解阶段 ===")
  570. test_goal = "写一篇关于AI智能体的毕业论文"
  571. print(f"目标: {test_goal}")
  572. result = decompose_agent.run(f"请拆解目标:{test_goal}")
  573. print(f"\nDecomposeAgent 响应:\n{result}")
  574. print("\n" + "=" * 60)
  575. print("=== 示例3: 决策阶段 ===")
  576. test_options = "学大模型, 学智能体"
  577. print(f"选项: {test_options}")
  578. result = decide_agent.run(f"选项:{test_options}")
  579. print(f"\nDecideAgent 响应:\n{result}")
  580. # ========================================
  581. # 主程序入口
  582. # ========================================
  583. if __name__ == "__main__":
  584. controller = ThinkFlowController()
  585. print("\n" + "=" * 60)
  586. print(" ThinkFlow - AI 思维教练")
  587. print("=" * 60)
  588. print("\n请选择运行模式:")
  589. print("1. 交互模式(完整工作流)")
  590. print("2. 演示模式(预定义示例)")
  591. choice = input("\n请输入选择 (1/2): ").strip()
  592. if choice == "1":
  593. controller.start()
  594. elif choice == "2":
  595. run_demo()
  596. else:
  597. print("无效选择,默认运行演示模式。")
  598. run_demo()