Parcourir la source

Organize business assistant knowledge sources by agent

wangxi il y a 2 semaines
Parent
commit
bcd49279a7

+ 2 - 0
README.md

@@ -12,6 +12,8 @@
 
 ## 当前状态
 
+原始知识按Agent分目录保存:现有营商助手的15份资料位于 `reference/营商助手/`,默认知识整理仅读取此目录;其他Agent后续使用同级独立目录。已有知识ID和逻辑来源标识保持,详见[原始资料目录](knowledge/README.md#原始资料目录)。
+
 2026-09-24 F117:新增青浦区介绍与新闻998条基础知识,已规整、生成LLM摘要并写入Milvus新快照4231条;独立复验与在线重启状态见[知识整理](knowledge/README.md)及[知识入库](knowledge/ingestion.md)。新闻仅作原文证据,历史措施不自动视为现行政策。
 
 2026-09-24 F116:惠企政策源已替换为25个政策、170个事项,原文与新字段完成规整;170条LLM摘要生成,Milvus新快照3233条独立回读通过。其他3063条摘要保持,旧集合保留;在线服务需重启读取新manifest,真实问答和政策有效性未验收。见[知识整理](knowledge/README.md)与[知识入库](knowledge/ingestion.md)。

+ 1 - 1
docs/git-upload.md

@@ -22,7 +22,7 @@
 
 ## 应保留
 
-- `reference/` 原始知识 JSON、CSV、Excel 等资料,按用户要求随仓库保留;其中 Office 临时锁文件 `~$*` 仍忽略。
+- `reference/` 按Agent分目录存放原始知识;现有15份资料在 `reference/营商助手/`,其他Agent后续分别使用同级目录。JSON、CSV、Excel 等资料按用户要求随仓库保留;其中 Office 临时锁文件 `~$*` 仍忽略。
 - `src/` 全部业务代码、提示词、分类配置与合成测试数据;`scripts/` 和 `init.ps1`。
 - `pyproject.toml`、`uv.lock`、`.python-version`、`.gitignore`。
 - `knowledge/` 中的 Markdown 说明和 `templates/` 模板。

+ 9 - 3
knowledge/README.md

@@ -1,5 +1,11 @@
 # 第一阶段知识整理
 
+## 原始资料目录
+
+现有营商助手的15份原始资料统一存放在 `reference/营商助手/`。默认整理命令只读取该目录;后续其他Agent的原始资料分别放在 `reference/` 下各自的同级目录,接入时单独配置读取和输出位置。
+
+本次只调整物理目录,文件名和内容保持不变。现有知识记录及下方历史记录中的 `reference/文件名` 是保留的逻辑来源标识,对应实际文件 `reference/营商助手/文件名`;记录ID、摘要缓存和现有向量库不因本次搬迁改变。不要以整个 `reference/` 作为营商助手的输入根,否则会递归混入其他Agent的资料。
+
 ## 2026-10-09 16:48 — F130 青浦介绍与新闻源更新入库
 
 最新青浦介绍与新闻源从998条调整为501条,保留内容未变、移除497条;其他来源3268条保持,现六库3769条、article824条。完整规整、摘要复用、实际入库与独立回读通过;原目录和集合保留,在线进程需重启加载新清单。
@@ -39,7 +45,7 @@
 
 `refresh_local`拒绝直接复用已改变原始字段的旧知识摘要。今后原有非Excel资料有变化时,仍需按原摘要流程更新;后续继续使用上述带--llm-workbook的增量入口,只处理工作簿并保留旧摘要;默认本地刷新及普通摘要入口会拒绝将已生成的LLM摘要降级为原文抽取。入库与在线检索统一使用BGE,重建及配置见[知识入库](ingestion.md)。
 
-这里保存四类知识模板和从本地 `reference/` 生成的完整知识集。未调用模型或外部网站,未判断政策当前是否有效。
+这里保存四类知识模板和从本地 `reference/营商助手/` 生成的完整知识集。未调用模型或外部网站,未判断政策当前是否有效。
 
 ## 阅读入口
 
@@ -82,14 +88,14 @@
 .\.venv\Scripts\python.exe -m unittest discover -s src/step1_knowledge_prepare/tests -v
 ```
 
-代码位于 `src/step1_knowledge_prepare/`,JSON/CSV/TXT解析不需要第三方依赖,Excel使用已有openpyxl。参数 `--source` 和 `--output` 可指定测试目录;来源路径标识使用相对于输入根的 `reference/` 逻辑前缀。
+代码位于 `src/step1_knowledge_prepare/`,JSON/CSV/TXT解析不需要第三方依赖,Excel使用已有openpyxl。参数 `--source` 和 `--output` 可指定测试目录;默认输入为 `reference/营商助手/`,输出仍为 `knowledge/prepared/`。来源路径标识使用相对于输入根的 `reference/` 逻辑前缀。
 默认只覆盖 `prepared/` 下四类 JSONL、四类 Markdown 和报告,模板不被重写。所有输入成功解析后才写出;未变化的文件跳过写入,其余使用独立临时文件替换,报告最后写出。
 写入失败会清理当前临时文件,但可能留下部分新输出,此时 `--verify` 会失败,应解除文件占用后重新运行;不承诺整批事务。强制终止进程可能留下 `.tmp` 文件,可在确认无任务运行后清理。
 未知格式、重复 JSON 键、乱码、CSV 列数不匹配会失败并返回非零,避免静默丢失资料。
 重复运行同一输入生成字节一致的输出;`--verify` 只读,重建预期结果并逐文件核对,检测源更新、输出遗漏和篡改。
 
 原始资料及整理后的真实知识留在本地,`prepared/` 已加入 Git 忽略规则;模板、代码与说明可版本管理。
-后续新增、替换资料应修改 `reference/` 后重新生成,勿直接修改自动生成正文。模板变动需同步映射代码和测试,并升级版本。
+后续新增、替换营商助手资料应修改 `reference/营商助手/` 后重新生成,勿直接修改自动生成正文。模板变动需同步映射代码和测试,并升级版本。
 当前交付只完成知识规整,尚未建立向量索引、检索和问答接口。
 
 ## 第二步

+ 0 - 0
reference/E企融金融产品列表.csv → reference/营商助手/E企融金融产品列表.csv


+ 0 - 0
reference/policy_knowledge_checked.json → reference/营商助手/policy_knowledge_checked.json


+ 0 - 0
reference/qa.json → reference/营商助手/qa.json


+ 0 - 0
reference/上海市营商环境一本通_20260714.json → reference/营商助手/上海市营商环境一本通_20260714.json


+ 0 - 0
reference/上海虹桥国际中央商务区管理委员会政策和申报指南.json → reference/营商助手/上海虹桥国际中央商务区管理委员会政策和申报指南.json


+ 0 - 0
reference/人才政策和服务.json → reference/营商助手/人才政策和服务.json


+ 0 - 0
reference/企业服务中心介绍和联系方式.json → reference/营商助手/企业服务中心介绍和联系方式.json


+ 0 - 0
reference/企呼我应六大服务板块.json → reference/营商助手/企呼我应六大服务板块.json


+ 0 - 0
reference/公安常见涉企政策一本通(草拟)-整编.xlsx → reference/营商助手/公安常见涉企政策一本通(草拟)-整编.xlsx


+ 0 - 0
reference/办事指南.json → reference/营商助手/办事指南.json


+ 0 - 0
reference/区政府规范性文件和部门街镇规范性文件.json → reference/营商助手/区政府规范性文件和部门街镇规范性文件.json


+ 0 - 0
reference/惠企政策.json → reference/营商助手/惠企政策.json


+ 0 - 0
reference/涉外政策.json → reference/营商助手/涉外政策.json


+ 0 - 0
reference/科技型企业与优质中小企业政策申报知识库.json → reference/营商助手/科技型企业与优质中小企业政策申报知识库.json


+ 0 - 0
reference/青浦区介绍和新闻.json → reference/营商助手/青浦区介绍和新闻.json


+ 1 - 1
scripts/tests/test_deploy_checkout.py

@@ -19,7 +19,7 @@ class CheckoutTests(unittest.TestCase):
         required = {'deploy.sh', '.python-version', 'pyproject.toml', 'uv.lock',
                     'scripts/deploy.py', 'scripts/deploy_models.py', 'scripts/check-runtime.py',
                     'src/step1_knowledge_prepare/summarize.py', 'src/step2_knowledge_ingest/__main__.py',
-                    'src/step4_api/__main__.py', 'reference/qa.json'}
+                    'src/step4_api/__main__.py', 'reference/营商助手/qa.json'}
         self.assertTrue(required <= names, required - names)
         with tempfile.TemporaryDirectory() as temporary:
             root = Path(temporary) / 'uploaded checkout'

+ 2 - 1
src/step1_knowledge_prepare/prepare.py

@@ -324,7 +324,8 @@ def run(source, output):
 
 def main(argv=None):
     parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument("--source", type=Path, default=ROOT / "reference")
+    parser.add_argument("--source", type=Path, default=ROOT / "reference" / "营商助手",
+                        help="raw source directory (default: reference/营商助手)")
     parser.add_argument("--output", type=Path, default=ROOT / "knowledge" / "prepared")
     parser.add_argument("--verify", action="store_true", help="verify existing output without rewriting")
     args = parser.parse_args(argv)

+ 22 - 1
src/step1_knowledge_prepare/tests/test_prepare.py

@@ -6,7 +6,7 @@ import unittest
 from unittest.mock import patch
 from pathlib import Path
 
-from src.step1_knowledge_prepare.prepare import ROOT, collect, normalize, run, verify_output
+from src.step1_knowledge_prepare.prepare import ROOT, collect, main, normalize, run, verify_output
 
 
 class PreparationTests(unittest.TestCase):
@@ -21,6 +21,27 @@ class PreparationTests(unittest.TestCase):
     def write_json(self, name, rows):
         (self.source / name).write_text(json.dumps(rows, ensure_ascii=False), encoding="utf-8")
 
+    def test_default_source_isolates_agent_and_preserves_logical_identity(self):
+        rows = [{"question": "合成问题", "answer": "合成答案"}]
+        self.write_json('qa.json', rows)
+        expected, expected_files = collect(self.source)
+        agent_source = self.source / '营商助手'
+        agent_source.mkdir()
+        (self.source / 'qa.json').rename(agent_source / 'qa.json')
+        other = self.source / '另一个助手'
+        other.mkdir()
+        (other / 'qa.json').write_text(json.dumps([
+            {"question": "其他助手的问题", "answer": "不可混入"}]), encoding='utf-8')
+        (self.source / 'unassigned.json').write_text('not valid JSON', encoding='utf-8')
+        with patch('src.step1_knowledge_prepare.prepare.ROOT', self.root), patch('sys.stdout', new_callable=io.StringIO):
+            self.assertEqual(main(['--output', str(self.output)]), 0)
+            self.assertEqual(main(['--output', str(self.output), '--verify']), 0)
+        actual = [json.loads(line) for line in (self.output / 'qa.jsonl').read_text(encoding='utf-8').splitlines()]
+        self.assertEqual(actual, expected)
+        report = json.loads((self.output / 'report.json').read_text(encoding='utf-8'))
+        self.assertEqual(report['files'], expected_files)
+        self.assertEqual(report['records'], 1)
+
     def test_policy_keeps_original_and_suggested_conditions_separate(self):
         raw = {"政策名称": "合成政策", "申报条件": "['条件甲', '条件乙']",
                "metadata": {"梳理后建议申报条件": ["建议丙"]}, "结束时间": "未标明"}