我设计了一个系统，内部有如下实体：

1. **文章**：以文本为主体，可能包含少量表格、代码、文头文尾等内容，包含大量需提取的关键信息。一个典型的示例：

    ```
    2026年6月1日晚8点，好友们决定为王若飞（男）组织一场生日派对。这次小范围聚会共有5人参加，刘阳（女）作为总策划，除寿星和总策划外，还有李四（女）、王五（男）和赵六（男）参加。活动总预算为3000元，且已完成备案，现场安排了掷骰子、扔飞镖和打桌球等破冰游戏，确保大家玩得尽兴。
    ```

2. **关键信息定义**：使用Json定义的树状结构，每个节点以`node_label`为key，包含`metadata`字段和`subnodes`字段，`metadata`字段中包含`node_label`（取关键信息名称的拼音首字母、`node_name`（关键信息名称）、`node_type`（取值为`concept`时表示分类节点，`property`时表示属性节点）、`value_type`（仅当`node_type`为`property`时生效，取值为`integer`、`float`、`string`、`boolean`、`date_time`、`class`，其中`class`表示关键信息为复合结构，包含子属性）、`is_list`（仅当`node_type`为`property`时生效，标识关键信息是否为列表类型，即是否可以有多个实例）字段。一个典型的示例：

    ```json
    {
      "metadata": {
        "node_label": "PDJH",
        "node_name": "派对计划",
        "node_type": "concept"
      },
      "subnodes": {
        "JBXX": {
          "metadata": {
            "node_label": "JBXX",
            "node_name": "基本信息",
            "node_type": "concept"
          },
          "subnodes": {
            "PDMC": {
              "metadata": {
                "node_label": "PDMC",
                "node_name": "派对名称",
                "node_type": "property",
                "value_type": "string",
                "is_list": false
              }
            },
            "PDKSSJ": {
              "metadata": {
                "node_label": "PDKSSJ",
                "node_name": "派对开始时间",
                "node_type": "property",
                "value_type": "date_time",
                "is_list": false
              }
            },
            "PDZRS": {
              "metadata": {
                "node_label": "PDZRS",
                "node_name": "派对总人数",
                "node_type": "property",
                "value_type": "integer",
                "is_list": false
              }
            },
            "SFBA": {
              "metadata": {
                "node_label": "SFBA",
                "node_name": "是否备案",
                "node_type": "property",
                "value_type": "boolean",
                "is_list": false
              }
            }
          }
        },
        "RYXX": {
          "metadata": {
            "node_label": "RYXX",
            "node_name": "人员信息",
            "node_type": "concept"
          },
          "subnodes": {
            "ZCH": {
              "metadata": {
                "node_label": "ZCH",
                "node_name": "总策划",
                "node_type": "property",
                "value_type": "class",
                "is_list": false
              },
              "subnodes": {
                "XM": {
                  "metadata": {
                    "node_label": "XM",
                    "node_name": "姓名",
                    "node_type": "property",
                    "value_type": "string",
                    "is_list": false
                  }
                },
                "SFZH": {
                  "metadata": {
                    "node_label": "SFZH",
                    "node_name": "身份证号",
                    "node_type": "property",
                    "value_type": "string",
                    "is_list": false
                  }
                }
              }
            },
            "RYQD": {
              "metadata": {
                "node_label": "RYQD",
                "node_name": "人员清单",
                "node_type": "property",
                "value_type": "class",
                "is_list": true
              },
              "subnodes": {
                "XM": {
                  "metadata": {
                    "node_label": "XM",
                    "node_name": "姓名",
                    "node_type": "property",
                    "value_type": "string",
                    "is_list": false
                  }
                },
                "SFZH": {
                  "metadata": {
                    "node_label": "SFZH",
                    "node_name": "身份证号",
                    "node_type": "property",
                    "value_type": "string",
                    "is_list": false
                  }
                }
              }
            }
          }
        },
        "HDXX": {
          "metadata": {
            "node_label": "HDXX",
            "node_name": "活动信息",
            "node_type": "concept"
          },
          "subnodes": {
            "PBYXLB": {
              "metadata": {
                "node_label": "PBYXLB",
                "node_name": "破冰游戏清单",
                "node_type": "property",
                "value_type": "string",
                "is_list": true
              }
            }
          }
        }
      }
    }
    ```

3. **关键信息实例**：根据**关键信息定义**，对其中的节点进行赋值而得到的实例，以Json形式表示。其中，有如下注意事项：

    1. 节点以`node_label`为key；
    
    2. 在**关键信息定义**中，`node_type`为`concept`的，value为Json对象，包含其子节点或子属性的值；

    3. 在**关键信息定义**中，`value_type`为`integer`、`float`、`string`、`boolean`、`date_time`时，如果`is_list`为`false`，value为对应类型；如果`is_list`为`true`，value为对应类型列表。特别地，`date_time`类型的值设置为字符串，但格式固定为`XXXX-XX-XXTXX:XX:XX.XXX`；

    4. 在**关键信息定义**中，`value_type`为`class`的，如果`is_list`为`false`，value为Json对象，包含其子属性的值；如果`is_list`为`true`，value为Json对象列表，每个元素包含其子属性的值。

    针对**文章**示例，根据上述**关键信息定义**，生成对应的**关键信息实例**为：

    ```json
    {
      "PDJH": {
        "JBXX": {
          "PDMC": "王若飞生日派对",
          "PDKSSJ": "2026-06-01T20:00:00.000",
          "PDZRS": 5,
          "PDZYS": 3000.0,
          "SFBA": true
        },
        "RYXX": {
          "ZCH": {
            "XM": "刘阳",
            "XB": "女"
          },
          "RYQD": [
            {
              "XM": "刘阳",
              "XB": "女"
            },
            {
              "XM": "王若飞",
              "XB": "男"
            },
            {
              "XM": "李四",
              "XB": "女"
            },
            {
              "XM": "王五",
              "XB": "男"
            },
            {
              "XM": "赵六",
              "XB": "男"
            }
          ]
        },
        "HDXX": {
          "PBYXQD": [
            "掷骰子",
            "扔飞镖",
            "打桌球"
          ]
        }
      }
    }
    ```

现在，我需要依托大模型，根据输入的**文章**和**关键信息定义**，从文章中将所有关键信息提取出来；请帮我设计一个prompt，使得大模型能够准确、高效、稳定地完成这个task。如果有必要，请使用langchain等框架，分步骤进行，以达到最好的效果。

--------

OK，现在，我需要将原始文本中相关的关键信息区域也标识出来。例如，针对“2026年6月1日晚8点，好友们决定为王若飞（男）组织一场生日派对，活动已完成备案。”这个文本，我希望生成类似于“{[PDJH.JBXX.PDKSSJ],2026-06-01T20:00:00.000,2026年6月1日晚8点,2026年1月1日~2026年12月31日,YYYY年M月D日(上午/下午/晚)H点[m分[s秒]]}，好友们决定为{[PDJH.RYXX.RYQD[0].XM],王若飞,王若飞,2~4个汉字,不作转换}（{[PDJH.RYXX.RYQD[0].XB],男,男,男/女,不作转换}）组织一场生日派对，活动{[PDJH.JBXX.SFBA],true,已完成备案,已完成备案/未完成备案,true->已完成备案/false->未完成备案}”。每块关键信息区域包括{节点路径,提取值,原始文本,提取值的约束（以自然语言表示，供大模型理解）,提取值到原始文本的转换规则（以自然语言表示，供大模型理解）}。请先帮我思考这个标识相关的符号体系是否合理，是否会与原文符号产生歧义，如果会，则不一定使用这套符号体系，改进该体系。

--------

1. 值约束来源是关键信息定义Json，部分关键信息会带一个constraints属性，里面会带{"raw_constraints":{"min_value":[0],"max_value":[100]}}、{"raw_constraints":{"enum_string":[["男", "女"]]}}这样的约束，确保将其精准地转换为自然语言，也可以在值约束里将原始的Json约束附带上。如果关键信息没有自带该属性，那么对于显而易见的关键信息约束，如“身份证号（18位数字，最后一位可以是X）”、“性别（男/女）”、“年龄（0-110）”等，你要进行推断，给出约束；对于你无法确定的约束，不要随意给出。  转换规则由你自行给出。
2. 标注以一个独立字段输出。

--------

有几个提取的原则需要微调一下：
1. 如果原文中某处同时对应关键信息定义中的多个关键信息，如“刘阳”即对应“总策划”，又对应“人员清单”中的一员，那么标注时，将多处同时标注，并在中间使用“||”分隔开，如：`{{PDJH.RYXX.ZCH.XM||刘阳||刘阳|| ||不作转换}}||{{PDJH.RYXX.RYQD[1].XM||刘阳||刘阳|| ||不作转换}}`；
2. 如果原文中不存在关键信息实例中的某处关键信息原文，例如原文为“为王若飞（男）组织一场生日派对”，而关键信息实例中为“王若飞生日派对”，那么在原文中找到最相关的部分，如“生日派对”进行标注，并将转换规则写为“根据原文进行微调”，结果为`{{PDJH.JBXX.PDMC||王若飞生日派对||生日派对|| ||根据原文进行微调}}`；
3. 对于时间、布尔等类型的值，你原来的做法是在代码里写死，我需要你根据原文内容，利用大模型（为确保快速处理，可不使用reasoning推理）给出明确的转换规则，例如`ISO 8601 → YYYY年M月D日(上午/下午/晚)H点[m分[s秒]]，根据是否为整点，可视情省略[]中的内容`；
4. 你现在预置了一些关键信息的约束，我同样要求你不要写死，根据不同关键信息定义中的内容，利用大模型（为确保快速处理，可不使用reasoning推理）给出相应的约束。

--------

针对某个提取值在原文中拥有多个对应文本的情况（如“王五”这个人在文中出现了多次），我新增了一条提取规则：“10. **同一提取值可多次标注**：如果某个提取值在原文中拥有多个对应文本，对这些文本分别进行标注，各标注的“节点路径”、“提取值”、“值约束”需保持一致，“原始文本”、“转换规则”可以不同，视原文决定”。但是经过测试，该规则未生效，多个对应文本只有一个被标注。请帮我检查并使之生效。

--------

请为我增加调试日志，在每一次与LLM交互前，打印本次交互是整个处理流程中的哪个分步骤，同时打印给LLM发送的prompt，以便我了解发送的内容。同时，在最外层函数调用处增加debug变量，只有当它为True时才打印上述日志。

--------

目前，在转换规则生成过程中，由于直接把整篇文章给了LLM，导致LLM识别提取值对应的原文不清晰（例如提取值“3000.0”对应文中的“3000”，但LLM认为是“3000元”）。所以，我想将“Step 2: LLM辅助生成约束和转换规则”拆分成两步，第一步将提取值对应的原文提取出来，第二步仅根据对应的原文进行约束和转换规则的生成。为了速度快，两步均采用大模型的轻量级调用，不调用reasoning推理。

--------

现在，我希望做一个界面，左侧为菜单栏，目前包含两个菜单：文档解析和信息管理。
1. 文档解析：分为两栏，左栏为文档展示区域，如未选择文档，显示“请选择文档”；上传文档后，左侧可浏览文档（支持pdf和word），右侧区域分为上下两栏，比例约为3:1，上方显示从文档中提取的正文内容（不含页眉页脚等，可转为markdown格式），右上方有一个按钮，“开始解析”，点击后，下栏开始输出正文内容关键信息提取的思考过程（即原python文件输出的内容）；思考完成后，上方内容区域变为提取后的文本（带{{||}}）。
2. 信息管理：针对关键信息树状结构的管理维护模块，以折叠树形式展示，每个节点均可编辑其元数据和子节点（参考之前的设计：**关键信息定义**：使用Json定义的树状结构，每个节点以`node_label`为key，包含`metadata`字段和`subnodes`字段，`metadata`字段中包含`node_label`（取关键信息名称的拼音首字母、`node_name`（关键信息名称）、`node_type`（取值为`concept`时表示分类节点，`property`时表示属性节点）、`value_type`（仅当`node_type`为`property`时生效，取值为`integer`、`float`、`string`、`boolean`、`date_time`、`class`，其中`class`表示关键信息为复合结构，包含子属性）、`is_list`（仅当`node_type`为`property`时生效，标识关键信息是否为列表类型，即是否可以有多个实例）字段。）
前端请使用vue实现，后端引入flask框架。请实现。

---

后端配置里有API Key，前端不用输入，请求发给后端，后端使用配置的API key即可。

---

将后端端口改为8754，前端端口改为8774，然后重启服务。

---

我要求左侧是文档预览，word打开什么样子就是什么样子，pdf打开什么样子就是什么样子；右侧md文字行距过大，减小一些

---

1. 上传pdf，报错：上传失败：文档解析失败: PyMuPDF 未安装，无法解析 PDF
2. 上传word，不报错，右侧也能解析出来，但左侧为空白页面，无word文档显示。

---

点击上传，报错：上传失败：Promise.withResolvers is not a function

---

页面缩放大小为125%时，显示没有问题；100%时，右侧有一大块区域是空白的，鼠标也无法交互。请修复。

---

信息管理中，我希望不止可以管理这一条关键信息，而是整理一个列表，每一条关键信息有名称、描述等元数据，点开后是这个树状结构，可以进行管理。支持json格式的导入，导入时填入名称和描述，导入格式参考@data/samples/info_definition.json。

---

我希望在@extractor.py中写一个函数，输入一段文本，从中提取**关键信息定义**Json，格式参考@data/samples/info_definition.json，具体含义：使用Json定义的树状结构，每个节点以`node_label`为key，包含`metadata`字段和`subnodes`字段，`metadata`字段中包含`node_label`（取关键信息名称的拼音首字母、`node_name`（关键信息名称）、`node_type`（取值为`concept`时表示分类节点，`property`时表示属性节点）、`value_type`（仅当`node_type`为`property`时生效，取值为`integer`、`float`、`string`、`boolean`、`date_time`、`class`，其中`class`表示关键信息为复合结构，包含子属性）、`is_list`（仅当`node_type`为`property`时生效，标识关键信息是否为列表类型，即是否可以有多个实例）字段。使用大模型来实现，生成后校验Json结构是否正确，如不正确，给出错误信息让大模型重试。

---

创建一个 test_definition.py 示例脚本，但复用test.py 中已有的 API 配置调用模型


---

右下方的文本框里完全没有思考过程输出，之前annotator.py、extractor.py等文件的思考过程输出呢？请将它们返回给前端，并实时流式输出至右下方文本框中。

---

写个启动脚本吧，里面设置INFO_EXTRACTOR_API_KEY变量，我稍后填入key。

---

1. 界面右下方，我希望将当前Step也输出出来，当前阶段的思考过程和输出前方统一加4个空格缩进（原有n个空格缩进的话，变为n+4个），阶段之间用“====================================”隔开，例如：
```
阶段一：XXXXX
    XXXX（思考过程）
    XXXX（思考过程）
    输出：
    {
      XXXXX
    }
====================================
阶段2a：XXXX
    ……
====================================
2. 增加功能：右侧上方区域，不再以markdown形式展示，直接将文字排版显示出来，类似于新闻页面，默认使用仿宋_GB2312字体。解析完成后，对于{{COL1||COL2||COL3||COL4||COL5}}中间的内容进行渲染，文字为原文（COL3），但使用灰色背景将对应文字框选。点击框选区域任意位置，弹出对话框，对话框内表单包含字段：
①关键信息名称，值为COL1对应中文字段名，不可编辑；②关键信息值，值为COL2，可编辑；这里你需要根据COL1对应的数据类型和COL4生成组件，如COL1为数字，这里就是数字输入框；COL1为时间，这里就是时间选择框；COL1为字符串，这里就是文本框；COL1为字符串，且COL4约束为枚举型，这里就是下拉选择框；等等。③文中展示值，值为COL3，可编辑，文本框。修改并确认后，后台更新{{}}中的COL2和COL3值，原文中灰色背景框选的部分更新为新的COL3值。

---

提取报错：提取失败：maximum recursion depth exceeded in comparison

---

基本实现了，但存在两个问题：
1. 灰色选中的区域，不要换行显示，嵌入原文中即可。我以[]表示灰色区域，示例：
```
[小明]是个[医生]。
```
而非
```
[小明]
是个
[医生]
。
```
2. 文本区域，段落与段落之间空行太多，去掉，一个空行都不要。

---

另外，右下方思考过程输出时，区域内滚动条自动不断滚到最底部吧

---

你个垃圾，灰色框选区域不要换行展示，而是嵌入文中，你一点没改；段落与段落之间一个空行都不要，但不是不要换行，还是要一个换行的。你行不行？你不行有的是大模型能干。

---

有没有一种可能，你用<span>，它灰色区域就一定会换行？我需要它在原文中的原位置，不能换行显示。

---

基本实现了，但段落之间的换行还要留着啊，怎么没了？从前后端都找找问题。
另外，每个段落前要缩进2字符。

---

当关键信息在段首时，你的处理方式会导致它前面的换行丢失，与上一段接在一起。请解决。
另外，为了测试方便，在文本区域增加按钮：导入文本，让我粘贴带{{}}的文本进去，你直接展示，省去标注流程。

---

段落间的换行又没了，你仔细看看怎么回事，我的要求：段落间的换行要留着，尤其注意段首有关键信息的情况；关键信息两侧的换行不要（除非它在段落首尾）

---

.replace(/(\{\{[^{}]*\}\})/g, (m) => m.replace(/\n+/g, ''))，这个语句是干什么用的？逻辑不对吧，执行完以后，文中就没有段落间的换行了

---

function confirmImport() {
  const text = importText.value || ''
  if (!text.trim()) {
    ElMessage.warning('请粘贴要导入的文本')
    return
  }
  annotatedContent.value = normalizeNewsText(text)
  hasResult.value = true
  importDialogVisible.value = false
  ElMessage.success('已导入文本')
  persistAnnotation()
}
这个方法中，normalizeNewsText(text)的结果含\n，为什么最终渲染出来的丢失了换行符？

---

导入时输入：
1
2
3
解析完后，也显示 1 2 3，换行到底怎么回事？？？

---

我希望启动前端，但页面上不显示vue调试工具

---

取消所有TODO。现在，信息管理中，条目无法删除，请实现。写完后，不要启动playwright验证。

---

把模型的model_name、base_url都做到start_backend的两个脚本里。

---

现在，我的test.py可以正常使用大模型推理，但使用start_backend.bat调用app.py中的推理，报错： {'error': {'code': '1113', 'message': '余额不足或无可用资源包,请充值。'}}，请帮我检查配置。

---

test.py 日志：POST https://open.bigmodel.cn/api/coding/paas/v4/chat/completions "HTTP/1.1 200 OK"
app.py 日志：POST https://open.bigmodel.cn/api/coding/paas/v4/chat/completions "HTTP/1.1 429 Too Many Requests"
你发个请求验证吧

---

三个问题：
1. 最终输出结果，前后容易带上<article>和</article>，过滤掉；
2. 对照我的原始需求：“右侧上方区域，不再以markdown形式展示，直接将文字排版显示出来，类似于新闻页面，默认使用仿宋_GB2312字体。解析完成后，对于{{COL1||COL2||COL3||COL4||COL5}}中间的内容进行渲染，文字为原文（COL3），但使用灰色背景将对应文字框选。点击框选区域任意位置，弹出对话框，对话框内表单包含字段：①关键信息名称，值为COL1对应中文字段名，不可编辑；②关键信息值，值为COL2，可编辑；这里你需要根据COL1对应的数据类型和COL4生成组件，如COL1为数字，这里就是数字输入框；COL1为时间，这里就是时间选择框；COL1为字符串，这里就是文本框；COL1为字符串，且COL4约束为枚举型，这里就是下拉选择框；等等。③文中展示值，值为COL3，可编辑，文本框。修改并确认后，后台更新{{}}中的COL2和COL3值，原文中灰色背景框选的部分更新为新的COL3值。” 你现在在灰色背景框选区域中，显示的是COL2的值，而非COL3的值；另外，弹出对话框中，“关键信息值”应为COL2的值，目前为空值；“文中展示值”应为COL3的值，目前为COL2的值。请修改；
3. 思考过程容易输出英文思考，请强调输出中文思考过程。

---

你说“LLM 有时输出 {{path||raw_text||...}}（第 2 段空、第 3 段填了原文），看起来像"COL2 显示成了 COL3"。” 根据我观察，不是的，就算{{}}中内容正确，前端处理也有问题。
另外，我不希望完全依赖大模型，请写一个方法，校验LLM输出的结果，{{}}外的内容+{{}}内的原文字段，能否完全匹配原文，若不能，进行相应修改后再输出。

---

这个新的修复步骤，是否在日志和前端思考过程输出中打印了？如果没有，请补充

---

重建报错： [修复] text-anchored 重建不可行：text segment 在原文中找不到（从位置 230 起）：'指挥员仍然感觉意犹未尽："每次训练，都是一次全新挑战！"\n\n'
就是因为LLM容易出现幻觉，比如，就算让LLM原样输出，它也容易把“”符号输出为""符号，或者出现一些小的问题。所以重建并不是只重建{{}}内的部分，还需要针对{{}}外的部分进行校验和修复，且保持最小的编辑距离。

---

“导入文本”和“提取完成”后，在渲染文本中的组件时，请把每个组件对应的关键信息值、文中展示值打印出来，我查看一下，现在还有问题。

---

@DocumentParse.vue文件中，第341行左右，
    if (ch === '|') {
      fields.push(buf)
      buf = ''
      continue
    }
这里有问题，分隔符为“||”而非“|”，修复它。这么明显的问题，为什么会出现？？？

---

