{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 构建第一个标准化患者智能体 —— API编程实操\n",
    "\n",
    "> **第二章 第五节 实操课** | 魔塔平台CPU环境\n",
    "\n",
    "本Notebook包含三个部分：\n",
    "1. **Part 1**: 单次API调用测试 —— 验证工作流连通性\n",
    "2. **Part 2**: 10次单轮对话 —— 自动化护理评估问答，保存到 `result.txt`\n",
    "3. **Part 3**: 多轮对话 —— 模拟真实问诊，AI能记住上下文\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 环境准备\n",
    "\n",
    "安装必要的Python库（魔塔平台只需安装 `requests`）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:14:06.267453Z",
     "iopub.status.busy": "2026-04-10T05:14:06.267287Z",
     "iopub.status.idle": "2026-04-10T05:14:09.272856Z",
     "shell.execute_reply": "2026-04-10T05:14:09.272088Z",
     "shell.execute_reply.started": "2026-04-10T05:14:06.267437Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
      "\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "!pip install requests -q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 配置信息\n",
    "\n",
    "**请将下面的配置替换为你自己的信息：**\n",
    "- `API_KEY`：在 https://www.coze.cn/open/oauth/pats 创建的个人访问令牌\n",
    "- `WORKFLOW_ID`：你的工作流URL中的 `workflow_id` 参数\n",
    "\n",
    "> 示例URL：`https://www.coze.cn/work_flow?workflow_id=7610xxx92420&space_id=74827xxxx99773491`\n",
    "> 其中 `76189xxx92420` 就是你的 workflow_id"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecutionIndicator": {
     "show": false
    },
    "execution": {
     "iopub.execute_input": "2026-04-10T07:01:13.196234Z",
     "iopub.status.busy": "2026-04-10T07:01:13.196062Z",
     "iopub.status.idle": "2026-04-10T07:01:13.234152Z",
     "shell.execute_reply": "2026-04-10T07:01:13.233621Z",
     "shell.execute_reply.started": "2026-04-10T07:01:13.196217Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "配置完成！\n",
      "API地址: https://api.coze.cn/v1/workflow/run\n",
      "工作流ID: 76267776xxx24089902\n",
      "API Key: pat_f76JZa8qYHtK57Ex...（已隐藏）\n"
     ]
    }
   ],
   "source": [
    "import requests\n",
    "import json\n",
    "import time\n",
    "from datetime import datetime\n",
    "\n",
    "# ============================================================\n",
    "# 请修改以下配置为你自己的信息\n",
    "# ============================================================\n",
    "API_KEY = \"pat_f76JZa8qYHtK57ExxxxxtUkDguwkcNW6fNzvNfS2aV4\"\n",
    "WORKFLOW_ID = \"76267776xxx24089902\"\n",
    "# ============================================================\n",
    "\n",
    "API_URL = \"https://api.coze.cn/v1/workflow/run\"\n",
    "HEADERS = {\n",
    "    \"Authorization\": f\"Bearer {API_KEY}\",\n",
    "    \"Content-Type\": \"application/json\"\n",
    "}\n",
    "\n",
    "print(\"配置完成！\")\n",
    "print(f\"API地址: {API_URL}\")\n",
    "print(f\"工作流ID: {WORKFLOW_ID}\")\n",
    "print(f\"API Key: {API_KEY[:20]}...（已隐藏）\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Part 1：单次调用测试\n",
    "\n",
    "先发一个问题，确认工作流能正常工作。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:14:13.393584Z",
     "iopub.status.busy": "2026-04-10T05:14:13.393411Z",
     "iopub.status.idle": "2026-04-10T05:14:17.119718Z",
     "shell.execute_reply": "2026-04-10T05:14:17.118991Z",
     "shell.execute_reply.started": "2026-04-10T05:14:13.393567Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "护士：你好，请问你叫什么名字？\n",
      "患者：……李文清。（手指绞着衣角）\n",
      "\n",
      "护士：您平时吃什么药？\n",
      "患者：（挠挠头）有个叫氨氯地平的降压药，还有个治糖尿病的二甲双胍。\n"
     ]
    }
   ],
   "source": [
    "def call_workflow(question):\n",
    "    \"\"\"调用Coze工作流，发送一个问题并返回回答\"\"\"\n",
    "    # 包装输入：明确告诉LLM这是护士的问题，必须回答\n",
    "    wrapped_input = (\n",
    "        f\"护士对你说：'{question}'\\n\"\n",
    "        f\"请针对护士的这个问题回答，1-3句话，不要回答其他内容。\"\n",
    "    )\n",
    "    \n",
    "    payload = {\n",
    "        \"workflow_id\": WORKFLOW_ID,\n",
    "        \"parameters\": {\n",
    "            \"USER_INPUT\": wrapped_input\n",
    "        }\n",
    "    }\n",
    "    \n",
    "    try:\n",
    "        response = requests.post(API_URL, headers=HEADERS, json=payload, timeout=60)\n",
    "        result = response.json()\n",
    "        \n",
    "        if result.get(\"code\") == 0:\n",
    "            parsed = json.loads(result[\"data\"])\n",
    "            return parsed.get(\"data\") or parsed.get(\"result\") or parsed.get(\"output\") or str(parsed)\n",
    "        else:\n",
    "            return f\"[错误] code={result.get('code')}, msg={result.get('msg')}\"\n",
    "    except requests.exceptions.Timeout:\n",
    "        return \"[错误] 请求超时，请重试\"\n",
    "    except Exception as e:\n",
    "        return f\"[错误] {str(e)}\"\n",
    "\n",
    "\n",
    "# 测试一次调用\n",
    "test_question = \"你好，请问你叫什么名字？\"\n",
    "print(f\"护士：{test_question}\")\n",
    "print(f\"患者：{call_workflow(test_question)}\")\n",
    "print()\n",
    "test_question2 = \"您平时吃什么药？\"\n",
    "print(f\"护士：{test_question2}\")\n",
    "print(f\"患者：{call_workflow(test_question2)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**检查点**：\n",
    "- 如果看到患者的回答 → 工作流连通成功，继续！\n",
    "- 如果看到 `[错误]` → 检查API Key和workflow_id是否正确，工作流是否已发布"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Part 2：10次单轮对话\n",
    "\n",
    "用10个护理评估常见问题，逐一调用工作流，收集AI患者的回答。\n",
    "\n",
    "> **注意**：每次调用是独立的，AI不会记得前一次对话的内容。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:14:20.413864Z",
     "iopub.status.busy": "2026-04-10T05:14:20.413703Z",
     "iopub.status.idle": "2026-04-10T05:14:20.417285Z",
     "shell.execute_reply": "2026-04-10T05:14:20.416667Z",
     "shell.execute_reply.started": "2026-04-10T05:14:20.413848Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "共 10 个问题，开始逐一调用...\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 准备10个护理评估问题\n",
    "questions = [\n",
    "    \"你好，我是你的责任护士小王，请问你叫什么名字？\",\n",
    "    \"您今年多大了？平时一个人住吗？\",\n",
    "    \"您来医院是看什么病的？\",\n",
    "    \"您平时吃什么药？\",\n",
    "    \"这些药一天吃几次？有没有什么副作用？\",\n",
    "    \"有没有忘记吃药的时候？大概多久忘一次？\",\n",
    "    \"您平时自己量血压吗？最近血压多少？\",\n",
    "    \"最近有没有头晕、乏力或者其他不舒服？\",\n",
    "    \"家里人知道您的病情吗？他们会帮您管药吗？\",\n",
    "    \"您对自己的健康状况担心吗？最担心什么？\"\n",
    "]\n",
    "\n",
    "print(f\"共 {len(questions)} 个问题，开始逐一调用...\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:14:23.674199Z",
     "iopub.status.busy": "2026-04-10T05:14:23.674040Z",
     "iopub.status.idle": "2026-04-10T05:14:50.524251Z",
     "shell.execute_reply": "2026-04-10T05:14:50.523548Z",
     "shell.execute_reply.started": "2026-04-10T05:14:23.674183Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--- 第 1/10 轮 ---\n",
      "护士：你好，我是你的责任护士小王，请问你叫什么名字？\n",
      "患者：……李文清。（指尖轻轻摩挲着病号服衣角）\n",
      "\n",
      "--- 第 2/10 轮 ---\n",
      "护士：您今年多大了？平时一个人住吗？\n",
      "患者：我七十了。（低头）平时一个人住，孩子们都在外地。\n",
      "\n",
      "--- 第 3/10 轮 ---\n",
      "护士：您来医院是看什么病的？\n",
      "患者：（搓了搓衣角）我血压和血糖都不稳，过来调调。\n",
      "\n",
      "--- 第 4/10 轮 ---\n",
      "护士：您平时吃什么药？\n",
      "患者：吃氨氯地平，还有二甲双胍。（挠挠后颈）\n",
      "\n",
      "--- 第 5/10 轮 ---\n",
      "护士：这些药一天吃几次？有没有什么副作用？\n",
      "患者：氨氯地平一天一次，二甲双胍一天两次。（挠挠后颈）副作用倒是没太留意，就是偶尔有点头晕。\n",
      "\n",
      "--- 第 6/10 轮 ---\n",
      "护士：有没有忘记吃药的时候？大概多久忘一次？\n",
      "患者：……有时候忘，（挠挠后颈）大概两三天就会漏一次晚上那顿降糖药。\n",
      "\n",
      "--- 第 7/10 轮 ---\n",
      "护士：您平时自己量血压吗？最近血压多少？\n",
      "患者：（挠挠头）有时候量……最近一次好像是150/90？记不太准了。\n",
      "\n",
      "--- 第 8/10 轮 ---\n",
      "护士：最近有没有头晕、乏力或者其他不舒服？\n",
      "患者：（揉了揉后颈）这两天早上起来头有点发懵，腿也软乎乎的……没敢跟孩子说。\n",
      "\n",
      "--- 第 9/10 轮 ---\n",
      "护士：家里人知道您的病情吗？他们会帮您管药吗？\n",
      "患者：（低头抠衣角）知道是知道……孩子们忙，哪好意思让他们天天盯着我吃药啊。\n",
      "\n",
      "--- 第 10/10 轮 ---\n",
      "护士：您对自己的健康状况担心吗？最担心什么？\n",
      "患者：（叹气）挺担心的……就怕病情控制不好，给远在外地的儿女添麻烦。\n",
      "\n",
      "==================================================\n",
      "全部完成！共 10 轮对话\n"
     ]
    }
   ],
   "source": [
    "# 循环调用工作流，收集回答\n",
    "results = []\n",
    "\n",
    "for i, question in enumerate(questions, 1):\n",
    "    print(f\"--- 第 {i}/10 轮 ---\")\n",
    "    print(f\"护士：{question}\")\n",
    "    \n",
    "    answer = call_workflow(question)\n",
    "    print(f\"患者：{answer}\")\n",
    "    print()\n",
    "    \n",
    "    results.append({\n",
    "        \"round\": i,\n",
    "        \"question\": question,\n",
    "        \"answer\": answer\n",
    "    })\n",
    "    \n",
    "    # 间隔1秒，避免请求过快\n",
    "    if i < len(questions):\n",
    "        time.sleep(1)\n",
    "\n",
    "print(\"=\"*50)\n",
    "print(f\"全部完成！共 {len(results)} 轮对话\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:14:57.968976Z",
     "iopub.status.busy": "2026-04-10T05:14:57.968819Z",
     "iopub.status.idle": "2026-04-10T05:14:57.987799Z",
     "shell.execute_reply": "2026-04-10T05:14:57.987335Z",
     "shell.execute_reply.started": "2026-04-10T05:14:57.968960Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "结果已保存到 result.txt\n",
      "\n",
      "--- 文件内容预览 ---\n",
      "\n",
      "============================================================\n",
      "标准化患者智能体 —— 单轮对话记录\n",
      "时间：2026-04-10 13:14:57\n",
      "工作流ID：7626777633124089902\n",
      "============================================================\n",
      "\n",
      "--- 第 1 轮对话 ---\n",
      "护士：你好，我是你的责任护士小王，请问你叫什么名字？\n",
      "患者：……李文清。（指尖轻轻摩挲着病号服衣角）\n",
      "\n",
      "--- 第 2 轮对话 ---\n",
      "护士：您今年多大了？平时一个人住吗？\n",
      "患者：我七十了。（低头）平时一个人住，孩子们都在外地。\n",
      "\n",
      "--- 第 3 轮对话 ---\n",
      "护士：您来医院是看什么病的？\n",
      "患者：（搓了搓衣角）我血压和血糖都不稳，过来调调。\n",
      "\n",
      "--- 第 4 轮对话 ---\n",
      "护士：您平时吃什么药？\n",
      "患者：吃氨氯地平，还有二甲双胍。（挠挠后颈）\n",
      "\n",
      "--- 第 5 轮对话 ---\n",
      "护士：这些药一天吃几次？有没有什么副作用？\n",
      "患者：氨氯地平一天一次，二甲双胍一天两次。（挠挠后颈）副作用倒是没太留意，就是偶尔有点头晕。\n",
      "\n",
      "--- 第 6 轮对话 ---\n",
      "护士：有没有忘记吃药的时候？大概多久忘一次？\n",
      "患者：……有时候忘，（挠挠后颈）大概两三天就会漏一次晚上那顿降糖药。\n",
      "\n",
      "--- 第 7 轮对话 ---\n",
      "护士：您平时自己量血压吗？最近血压多少？\n",
      "患者：（挠挠头）有时候量……最近一次好像是150/90？记不太准了。\n",
      "\n",
      "--- 第 8 轮对话 ---\n",
      "护士：最近有没有头晕、乏力或者其他不舒服？\n",
      "患者：（揉了揉后颈）这两天早上起来头有点发懵，腿也软乎乎的……没敢跟孩子说。\n",
      "\n",
      "--- 第 9 轮对话 ---\n",
      "护士：家里人知道您的病情吗？他们会帮您管药吗？\n",
      "患者：（低头抠衣角）知道是知道……孩子们忙，哪好意思让他们天天盯着我吃药啊。\n",
      "\n",
      "--- 第 10 轮对话 ---\n",
      "护士：您对自己的健康状况担心吗？最担心什么？\n",
      "患者：（叹气）挺担心的……就怕病情控制不好，给远在外地的儿女添麻烦。\n",
      "\n",
      "============================================================\n",
      "记录结束\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 保存结果到 result.txt\n",
    "timestamp = datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n",
    "\n",
    "with open(\"result.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"=\" * 60 + \"\\n\")\n",
    "    f.write(\"标准化患者智能体 —— 单轮对话记录\\n\")\n",
    "    f.write(f\"时间：{timestamp}\\n\")\n",
    "    f.write(f\"工作流ID：{WORKFLOW_ID}\\n\")\n",
    "    f.write(\"=\" * 60 + \"\\n\\n\")\n",
    "    \n",
    "    for item in results:\n",
    "        f.write(f\"--- 第 {item['round']} 轮对话 ---\\n\")\n",
    "        f.write(f\"护士：{item['question']}\\n\")\n",
    "        f.write(f\"患者：{item['answer']}\\n\\n\")\n",
    "    \n",
    "    f.write(\"=\" * 60 + \"\\n\")\n",
    "    f.write(\"记录结束\\n\")\n",
    "\n",
    "print(\"结果已保存到 result.txt\")\n",
    "print(\"\\n--- 文件内容预览 ---\\n\")\n",
    "\n",
    "with open(\"result.txt\", \"r\", encoding=\"utf-8\") as f:\n",
    "    print(f.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Part 3：多轮对话 —— 模拟真实问诊\n",
    "\n",
    "### 原理说明\n",
    "\n",
    "单轮对话中，每次调用都是独立的，AI不知道之前说了什么。\n",
    "\n",
    "多轮对话的核心思路：**每次调用时，把之前的对话历史一起发送给AI**。\n",
    "\n",
    "```\n",
    "第1轮输入: \"护士：你吃什么药？\"\n",
    "第1轮输出: \"降压药和二甲双胍\"\n",
    "\n",
    "第2轮输入: \"对话历史：\\n护士：你吃什么药？\\n患者：降压药和二甲双胍\\n---\\n护士：二甲双胍一天吃几次？\"\n",
    "第2轮输出: \"医生说一天两次……\"\n",
    "```\n",
    "\n",
    "这样AI就能\"看到\"之前的对话，实现上下文连贯。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:15:03.054970Z",
     "iopub.status.busy": "2026-04-10T05:15:03.054812Z",
     "iopub.status.idle": "2026-04-10T05:15:03.060600Z",
     "shell.execute_reply": "2026-04-10T05:15:03.060053Z",
     "shell.execute_reply.started": "2026-04-10T05:15:03.054954Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "多轮对话函数已定义！\n"
     ]
    }
   ],
   "source": [
    "def call_workflow_with_history(question, history):\n",
    "    \"\"\"带对话历史的工作流调用（多轮对话）\n",
    "    \n",
    "    参数：\n",
    "        question: 当前护士的提问\n",
    "        history: 之前的对话历史列表 [{\"role\":\"护士\",\"content\":\"...\"}, {\"role\":\"患者\",\"content\":\"...\"}]\n",
    "    返回：\n",
    "        AI患者的回答\n",
    "    \"\"\"\n",
    "    if history:\n",
    "        # 构建结构化输入：历史 + 新问题 + 明确指令\n",
    "        lines = []\n",
    "        lines.append(\"你是患者李文清。以下是你刚才和护士的对话：\")\n",
    "        lines.append(\"\")\n",
    "        for msg in history:\n",
    "            lines.append(f\"{msg['role']}：{msg['content']}\")\n",
    "        lines.append(\"\")\n",
    "        lines.append(f\"护士现在又问你：'{question}'\")\n",
    "        lines.append(\"\")\n",
    "        lines.append(\"请针对护士的这个新问题回答，1-3句话。\")\n",
    "        lines.append(\"如果之前提到过相关信息（如药名），请保持一致。\")\n",
    "        full_input = \"\\n\".join(lines)\n",
    "    else:\n",
    "        # 第一轮：和单轮一样包装\n",
    "        full_input = (\n",
    "            f\"护士对你说：'{question}'\\n\"\n",
    "            f\"请针对护士的这个问题回答，1-3句话，不要回答其他内容。\"\n",
    "        )\n",
    "    \n",
    "    # 直接调用工作流\n",
    "    payload = {\n",
    "        \"workflow_id\": WORKFLOW_ID,\n",
    "        \"parameters\": {\"USER_INPUT\": full_input}\n",
    "    }\n",
    "    try:\n",
    "        response = requests.post(API_URL, headers=HEADERS, json=payload, timeout=60)\n",
    "        result = response.json()\n",
    "        if result.get(\"code\") == 0:\n",
    "            parsed = json.loads(result[\"data\"])\n",
    "            return parsed.get(\"data\") or parsed.get(\"result\") or parsed.get(\"output\") or str(parsed)\n",
    "        else:\n",
    "            return f\"[错误] code={result.get('code')}, msg={result.get('msg')}\"\n",
    "    except Exception as e:\n",
    "        return f\"[错误] {str(e)}\"\n",
    "\n",
    "\n",
    "print(\"多轮对话函数已定义！\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:15:06.014106Z",
     "iopub.status.busy": "2026-04-10T05:15:06.013921Z",
     "iopub.status.idle": "2026-04-10T05:15:23.489874Z",
     "shell.execute_reply": "2026-04-10T05:15:23.489345Z",
     "shell.execute_reply.started": "2026-04-10T05:15:06.014089Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "开始多轮对话（用药问诊场景）\n",
      "==================================================\n",
      "\n",
      "--- 第 1 轮 ---\n",
      "护士：你好李文清，我是护士小王。你平时在吃什么药啊？\n",
      "患者：吃氨氯地平降压，还有二甲双胍治糖尿病。（挠了挠后颈）\n",
      "\n",
      "--- 第 2 轮 ---\n",
      "护士：这个降压药叫什么名字，你记得吗？\n",
      "患者：氨氯地平，医生开的那个降压药。（指尖蹭了蹭衣角）\n",
      "\n",
      "--- 第 3 轮 ---\n",
      "护士：那糖尿病的药呢，叫什么？一天吃几次？\n",
      "患者：叫二甲双胍，一天吃两次。（低头抠了抠裤腿）\n",
      "\n",
      "--- 第 4 轮 ---\n",
      "护士：你刚才说的那个糖尿病的药，吃了有没有什么不舒服？\n",
      "患者：（叹气）没啥特别不舒服的……就是有时候胃里有点胀，忍忍就过去了。\n",
      "\n",
      "--- 第 5 轮 ---\n",
      "护士：那降压药呢，吃了之后感觉怎么样？有没有头晕？\n",
      "患者：（抿了抿嘴）降压药吃着还行……偶尔会有点头沉，歇会儿就好了。\n",
      "\n",
      "--- 第 6 轮 ---\n",
      "护士：你有没有忘记吃药的时候？一般什么时候容易忘？\n",
      "患者：……有时候会忘。（低头）晚上那顿治糖尿病的药，经常记不起来。\n",
      "\n",
      "--- 第 7 轮 ---\n",
      "护士：你刚才提到的那两种药，你能再说一遍名字吗？\n",
      "患者：氨氯地平，还有二甲双胍。（抠了抠手背）\n",
      "\n",
      "==================================================\n",
      "多轮对话完成！共 7 轮\n"
     ]
    }
   ],
   "source": [
    "# 多轮对话：用药问诊场景\n",
    "multi_turn_questions = [\n",
    "    \"你好李文清，我是护士小王。你平时在吃什么药啊？\",\n",
    "    \"这个降压药叫什么名字，你记得吗？\",\n",
    "    \"那糖尿病的药呢，叫什么？一天吃几次？\",\n",
    "    \"你刚才说的那个糖尿病的药，吃了有没有什么不舒服？\",\n",
    "    \"那降压药呢，吃了之后感觉怎么样？有没有头晕？\",\n",
    "    \"你有没有忘记吃药的时候？一般什么时候容易忘？\",\n",
    "    \"你刚才提到的那两种药，你能再说一遍名字吗？\"\n",
    "]\n",
    "\n",
    "# 执行多轮对话\n",
    "history = []\n",
    "multi_turn_results = []\n",
    "\n",
    "print(\"开始多轮对话（用药问诊场景）\")\n",
    "print(\"=\" * 50)\n",
    "\n",
    "for i, question in enumerate(multi_turn_questions, 1):\n",
    "    print(f\"\\n--- 第 {i} 轮 ---\")\n",
    "    print(f\"护士：{question}\")\n",
    "    \n",
    "    # 调用带历史的工作流\n",
    "    answer = call_workflow_with_history(question, history)\n",
    "    print(f\"患者：{answer}\")\n",
    "    \n",
    "    # 更新对话历史\n",
    "    history.append({\"role\": \"护士\", \"content\": question})\n",
    "    history.append({\"role\": \"患者\", \"content\": answer})\n",
    "    \n",
    "    multi_turn_results.append({\n",
    "        \"round\": i,\n",
    "        \"question\": question,\n",
    "        \"answer\": answer\n",
    "    })\n",
    "    \n",
    "    # 间隔1秒\n",
    "    if i < len(multi_turn_questions):\n",
    "        time.sleep(1)\n",
    "\n",
    "print(\"\\n\" + \"=\" * 50)\n",
    "print(f\"多轮对话完成！共 {len(multi_turn_results)} 轮\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:15:25.415468Z",
     "iopub.status.busy": "2026-04-10T05:15:25.415300Z",
     "iopub.status.idle": "2026-04-10T05:15:25.432513Z",
     "shell.execute_reply": "2026-04-10T05:15:25.431826Z",
     "shell.execute_reply.started": "2026-04-10T05:15:25.415452Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "多轮对话结果已保存到 result_multi_turn.txt\n",
      "\n",
      "--- 文件内容预览 ---\n",
      "\n",
      "============================================================\n",
      "标准化患者智能体 —— 多轮对话记录（用药问诊）\n",
      "时间：2026-04-10 13:15:25\n",
      "工作流ID：7626777633124089902\n",
      "============================================================\n",
      "\n",
      "--- 第 1 轮对话 ---\n",
      "护士：你好李文清，我是护士小王。你平时在吃什么药啊？\n",
      "患者：吃氨氯地平降压，还有二甲双胍治糖尿病。（挠了挠后颈）\n",
      "\n",
      "--- 第 2 轮对话 ---\n",
      "护士：这个降压药叫什么名字，你记得吗？\n",
      "患者：氨氯地平，医生开的那个降压药。（指尖蹭了蹭衣角）\n",
      "\n",
      "--- 第 3 轮对话 ---\n",
      "护士：那糖尿病的药呢，叫什么？一天吃几次？\n",
      "患者：叫二甲双胍，一天吃两次。（低头抠了抠裤腿）\n",
      "\n",
      "--- 第 4 轮对话 ---\n",
      "护士：你刚才说的那个糖尿病的药，吃了有没有什么不舒服？\n",
      "患者：（叹气）没啥特别不舒服的……就是有时候胃里有点胀，忍忍就过去了。\n",
      "\n",
      "--- 第 5 轮对话 ---\n",
      "护士：那降压药呢，吃了之后感觉怎么样？有没有头晕？\n",
      "患者：（抿了抿嘴）降压药吃着还行……偶尔会有点头沉，歇会儿就好了。\n",
      "\n",
      "--- 第 6 轮对话 ---\n",
      "护士：你有没有忘记吃药的时候？一般什么时候容易忘？\n",
      "患者：……有时候会忘。（低头）晚上那顿治糖尿病的药，经常记不起来。\n",
      "\n",
      "--- 第 7 轮对话 ---\n",
      "护士：你刚才提到的那两种药，你能再说一遍名字吗？\n",
      "患者：氨氯地平，还有二甲双胍。（抠了抠手背）\n",
      "\n",
      "============================================================\n",
      "\n",
      "观察要点：\n",
      "1. 第1轮提到的药物名称，后面几轮是否保持一致？\n",
      "2. 第4轮提到'刚才说的药'，AI能否正确关联？\n",
      "3. 第7轮要求复述药名，AI是否记得之前说过什么？\n",
      "4. 整体对话是否自然连贯，像真实问诊？\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 保存多轮对话结果\n",
    "timestamp = datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\n",
    "\n",
    "with open(\"result_multi_turn.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"=\" * 60 + \"\\n\")\n",
    "    f.write(\"标准化患者智能体 —— 多轮对话记录（用药问诊）\\n\")\n",
    "    f.write(f\"时间：{timestamp}\\n\")\n",
    "    f.write(f\"工作流ID：{WORKFLOW_ID}\\n\")\n",
    "    f.write(\"=\" * 60 + \"\\n\\n\")\n",
    "    \n",
    "    for item in multi_turn_results:\n",
    "        f.write(f\"--- 第 {item['round']} 轮对话 ---\\n\")\n",
    "        f.write(f\"护士：{item['question']}\\n\")\n",
    "        f.write(f\"患者：{item['answer']}\\n\\n\")\n",
    "    \n",
    "    f.write(\"=\" * 60 + \"\\n\")\n",
    "    f.write(\"\\n观察要点：\\n\")\n",
    "    f.write(\"1. 第1轮提到的药物名称，后面几轮是否保持一致？\\n\")\n",
    "    f.write(\"2. 第4轮提到'刚才说的药'，AI能否正确关联？\\n\")\n",
    "    f.write(\"3. 第7轮要求复述药名，AI是否记得之前说过什么？\\n\")\n",
    "    f.write(\"4. 整体对话是否自然连贯，像真实问诊？\\n\")\n",
    "\n",
    "print(\"多轮对话结果已保存到 result_multi_turn.txt\")\n",
    "print(\"\\n--- 文件内容预览 ---\\n\")\n",
    "\n",
    "with open(\"result_multi_turn.txt\", \"r\", encoding=\"utf-8\") as f:\n",
    "    print(f.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## 拓展：自由多轮对话（交互式）\n",
    "\n",
    "下面的代码让你可以像聊天一样，手动输入问题与AI患者对话。\n",
    "\n",
    "> 输入 `quit` 或 `退出` 结束对话。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-10T05:15:30.042214Z",
     "iopub.status.busy": "2026-04-10T05:15:30.042047Z",
     "iopub.status.idle": "2026-04-10T05:15:42.930646Z",
     "shell.execute_reply": "2026-04-10T05:15:42.930071Z",
     "shell.execute_reply.started": "2026-04-10T05:15:30.042198Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================================\n",
      "交互式多轮对话 —— 你扮演护士，AI扮演患者\n",
      "输入 quit 或 退出 结束对话\n",
      "==================================================\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "\n",
      "护士： 你好\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "患者：你好……（抬手挠了挠后颈）\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "\n",
      "护士： quit\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "对话结束。\n",
      "\n",
      "共进行了 1 轮对话\n"
     ]
    }
   ],
   "source": [
    "# 交互式多轮对话\n",
    "print(\"=\" * 50)\n",
    "print(\"交互式多轮对话 —— 你扮演护士，AI扮演患者\")\n",
    "print(\"输入 quit 或 退出 结束对话\")\n",
    "print(\"=\" * 50)\n",
    "\n",
    "interactive_history = []\n",
    "round_num = 0\n",
    "\n",
    "while True:\n",
    "    user_input = input(\"\\n护士：\").strip()\n",
    "    \n",
    "    if user_input.lower() in [\"quit\", \"exit\", \"退出\", \"q\"]:\n",
    "        print(\"\\n对话结束。\")\n",
    "        break\n",
    "    \n",
    "    if not user_input:\n",
    "        print(\"（请输入你的问题）\")\n",
    "        continue\n",
    "    \n",
    "    round_num += 1\n",
    "    answer = call_workflow_with_history(user_input, interactive_history)\n",
    "    print(f\"患者：{answer}\")\n",
    "    \n",
    "    interactive_history.append({\"role\": \"护士\", \"content\": user_input})\n",
    "    interactive_history.append({\"role\": \"患者\", \"content\": answer})\n",
    "\n",
    "print(f\"\\n共进行了 {round_num} 轮对话\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## 实操完成！\n",
    "\n",
    "### 你今天学会了：\n",
    "1. 通过API调用Coze工作流（`requests.post`）\n",
    "2. 批量自动化对话并保存结果\n",
    "3. 通过拼接对话历史实现多轮对话\n",
    "\n",
    "### 生成的文件：\n",
    "- `result.txt` —— 10次单轮对话记录\n",
    "- `result_multi_turn.txt` —— 多轮用药问诊记录\n",
    "\n",
    "### 下一步：\n",
    "- 修改Prompt创建不同人格的患者\n",
    "- 设计更多问诊场景的多轮对话\n",
    "- 第三章将学习人格一致性评价方法"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
