test-ai-one 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
test_ai_one-0.0.1/.env ADDED
@@ -0,0 +1,5 @@
1
+ API_BASE=https://api.deepseek.com
2
+ MODEL_NAME=deepseek-v4-flash
3
+ API_KEY=sk-1635f68b539e4af5b9c0e5b80470090c
4
+
5
+ OUTPUT_DIR=./output
@@ -0,0 +1,4 @@
1
+ API_BASE=https://api.openai.com/v1
2
+ MODEL_NAME=gpt-3.5-turbo
3
+ API_KEY=your_api_key_here
4
+ OUTPUT_DIR=./output
@@ -0,0 +1,484 @@
1
+ Metadata-Version: 2.4
2
+ Name: test-ai-one
3
+ Version: 0.0.1
4
+ Summary: 一个用于学习 Python 公共库构建和发布的示例工程
5
+ Requires-Python: >=3.14
6
+ Requires-Dist: aiofiles==25.1.0
7
+ Requires-Dist: anyio==4.14.0
8
+ Requires-Dist: certifi==2026.6.17
9
+ Requires-Dist: h11==0.16.0
10
+ Requires-Dist: httpcore==1.0.9
11
+ Requires-Dist: httpx==0.28.1
12
+ Requires-Dist: idna==3.18
13
+ Requires-Dist: markdown==3.10.2
14
+ Requires-Dist: packaging==26.2
15
+ Requires-Dist: python-dateutil==2.9.0.post0
16
+ Requires-Dist: python-dotenv==1.2.2
17
+ Requires-Dist: six==1.17.0
18
+ Description-Content-Type: text/markdown
19
+
20
+ # 多智能体协同调研系统代码阅读指南
21
+
22
+ 这是一个用于演示 Python 异步编程的轻量级调研项目。用户在命令行输入一个调研主题后,程序会调用大模型把主题拆成多个子任务,再并发执行这些子任务,最后合并结果、生成 Markdown 调研报告并保存到本地。
23
+
24
+ 项目代码规模较小,但模块边界比较清晰,适合按“入口 -> 编排 -> 业务节点 -> 底层工具”的顺序学习。
25
+
26
+ ## 一、项目功能概览
27
+
28
+ 项目的核心流程如下:
29
+
30
+ 1. 读取用户输入的调研需求。
31
+ 2. 调用大模型,将调研需求分解成 3-5 个相互独立的子任务。
32
+ 3. 使用 `asyncio.gather` 并发执行所有子任务。
33
+ 4. 将各子任务结果拼接成中间材料。
34
+ 5. 再次调用大模型,把中间材料整理成完整 Markdown 报告。
35
+ 6. 使用异步文件 I/O 将报告保存到 `output` 目录。
36
+
37
+ 整体上它是一个“多智能体协同调研”的教学型项目,其中每个子任务可以理解为一个独立的调研助手。
38
+
39
+ ## 二、目录结构
40
+
41
+ ```text
42
+ .
43
+ ├── README.md
44
+ ├── requirements.txt
45
+ ├── .env.example
46
+ ├── output/
47
+ └── src/
48
+ ├── __init__.py
49
+ ├── main.py
50
+ ├── orchestrator.py
51
+ ├── decomposer.py
52
+ ├── worker.py
53
+ ├── merger.py
54
+ ├── summarizer.py
55
+ ├── reporter.py
56
+ ├── ai_tools.py
57
+ └── config.py
58
+ ```
59
+
60
+ ## 三、运行方式
61
+
62
+ ### 1. 安装依赖
63
+
64
+ ```bash
65
+ pip install -r requirements.txt
66
+ ```
67
+
68
+ 项目依赖:
69
+
70
+ ```text
71
+ httpx>=0.27.0
72
+ python-dotenv>=1.0.0
73
+ aiofiles>=24.1.0
74
+ ```
75
+
76
+ ### 2. 配置环境变量
77
+
78
+ 复制 `.env.example` 为 `.env`,然后填入真实配置:
79
+
80
+ ```bash
81
+ cp .env.example .env
82
+ ```
83
+
84
+ `.env.example` 中包含以下配置:
85
+
86
+ ```env
87
+ API_BASE=https://api.openai.com/v1
88
+ MODEL_NAME=gpt-3.5-turbo
89
+ API_KEY=your_api_key_here
90
+ OUTPUT_DIR=./output
91
+ ```
92
+
93
+ 配置说明:
94
+
95
+ | 变量名 | 作用 | 默认值 |
96
+ | --- | --- | --- |
97
+ | `API_BASE` | OpenAI 兼容接口地址 | `https://api.openai.com/v1` |
98
+ | `MODEL_NAME` | 调用的模型名称 | `gpt-3.5-turbo` |
99
+ | `API_KEY` | API 密钥,必须配置 | 空字符串 |
100
+ | `OUTPUT_DIR` | 报告输出目录 | `./output` |
101
+
102
+ ### 3. 启动程序
103
+
104
+ ```bash
105
+ python -m src.main
106
+ ```
107
+
108
+ 运行后根据提示输入调研需求,例如:
109
+
110
+ ```text
111
+ 请输入调研需求: 调研 Python 异步编程在 Web 服务中的应用
112
+ ```
113
+
114
+ 报告会保存到 `OUTPUT_DIR` 指定的目录中,默认是 `./output`。
115
+
116
+ ## 四、模块职责
117
+
118
+ | 模块 | 主要职责 | 关键函数/变量 |
119
+ | --- | --- | --- |
120
+ | `src/main.py` | 命令行入口,读取用户输入,启动异步主流程 | `main()` |
121
+ | `src/orchestrator.py` | 流程编排层,串联任务分解、并发执行、合并、总结、保存 | `run_pipeline()` |
122
+ | `src/decomposer.py` | 调用大模型进行任务分解,并解析 JSON 数组结果 | `decompose()`、`_parse_json_array()` |
123
+ | `src/worker.py` | 执行单个调研子任务 | `execute_subtask()` |
124
+ | `src/merger.py` | 将子任务、子任务结果合并成 Markdown 中间材料 | `merge_results()` |
125
+ | `src/summarizer.py` | 调用大模型生成最终调研报告 | `summarize()` |
126
+ | `src/reporter.py` | 创建输出目录,并异步写入 Markdown 报告文件 | `save_report()` |
127
+ | `src/ai_tools.py` | OpenAI 兼容 Chat Completions 流式调用封装 | `completions()`、`get_text()` |
128
+ | `src/config.py` | 加载 `.env` 配置并暴露项目配置常量 | `API_BASE`、`MODEL_NAME`、`API_KEY`、`OUTPUT_DIR` |
129
+
130
+ ## 五、模块依赖关系
131
+
132
+ 从调用方向看,依赖关系如下:
133
+
134
+ ```text
135
+ src/main.py
136
+ └── src/orchestrator.py
137
+ ├── src/decomposer.py
138
+ │ └── src/ai_tools.py
139
+ │ └── src/config.py
140
+ ├── src/worker.py
141
+ │ └── src/ai_tools.py
142
+ │ └── src/config.py
143
+ ├── src/merger.py
144
+ ├── src/summarizer.py
145
+ │ └── src/ai_tools.py
146
+ │ └── src/config.py
147
+ └── src/reporter.py
148
+ └── src/config.py
149
+ ```
150
+
151
+ 更抽象地看,可以分成四层:
152
+
153
+ ```text
154
+ 入口层
155
+ main.py
156
+
157
+ 编排层
158
+ orchestrator.py
159
+
160
+ 业务节点层
161
+ decomposer.py
162
+ worker.py
163
+ merger.py
164
+ summarizer.py
165
+ reporter.py
166
+
167
+ 基础设施层
168
+ ai_tools.py
169
+ config.py
170
+ ```
171
+
172
+ ### 外部依赖关系
173
+
174
+ | 外部库 | 被哪个模块使用 | 用途 |
175
+ | --- | --- | --- |
176
+ | `httpx` | `src/ai_tools.py` | 异步 HTTP 客户端,请求 Chat Completions 接口 |
177
+ | `python-dotenv` | `src/config.py` | 读取 `.env` 文件中的环境变量 |
178
+ | `aiofiles` | `src/reporter.py` | 异步写入报告文件 |
179
+ | Python 标准库 `asyncio` | `src/main.py`、`src/orchestrator.py` | 启动事件循环、并发执行子任务 |
180
+ | Python 标准库 `json` | `src/ai_tools.py`、`src/decomposer.py` | 解析流式响应和任务分解结果 |
181
+ | Python 标准库 `re` | `src/decomposer.py` | 从非标准输出中提取 JSON 数组 |
182
+ | Python 标准库 `os` | `src/config.py`、`src/reporter.py` | 读取环境变量、创建输出目录 |
183
+ | Python 标准库 `datetime` | `src/reporter.py` | 生成报告文件时间戳 |
184
+
185
+ ## 六、主流程时序
186
+
187
+ 一次完整运行的函数调用顺序如下:
188
+
189
+ ```text
190
+ python -m src.main
191
+ |
192
+ v
193
+ main()
194
+ |
195
+ v
196
+ run_pipeline(topic)
197
+ |
198
+ +--> decompose(topic)
199
+ | |
200
+ | +--> get_text(messages)
201
+ | |
202
+ | +--> completions(messages)
203
+ |
204
+ +--> asyncio.gather(...)
205
+ | |
206
+ | +--> execute_subtask(index, total, subtask)
207
+ | |
208
+ | +--> get_text(messages)
209
+ | |
210
+ | +--> completions(messages)
211
+ |
212
+ +--> merge_results(topic, subtasks, results)
213
+ |
214
+ +--> summarize(topic, merged)
215
+ | |
216
+ | +--> get_text(messages)
217
+ | |
218
+ | +--> completions(messages)
219
+ |
220
+ +--> save_report(topic, report)
221
+ |
222
+ +--> aiofiles.open(...).write(...)
223
+ ```
224
+
225
+ ## 七、推荐阅读顺序
226
+
227
+ ### 第 1 步:先看入口和总流程
228
+
229
+ 建议先读:
230
+
231
+ 1. `src/main.py`
232
+ 2. `src/orchestrator.py`
233
+
234
+ 阅读目标:
235
+
236
+ - 理解程序如何启动。
237
+ - 理解 `asyncio.run(main())` 的作用。
238
+ - 理解 `run_pipeline()` 如何把整个调研流程拆成 4 个步骤。
239
+ - 重点关注 `asyncio.gather(..., return_exceptions=True)`,这是项目异步并发的核心。
240
+
241
+ `orchestrator.py` 是全项目最适合先读的文件,因为它展示了所有模块之间的协作方式。
242
+
243
+ ### 第 2 步:看任务分解模块
244
+
245
+ 继续读:
246
+
247
+ 1. `src/decomposer.py`
248
+
249
+ 阅读目标:
250
+
251
+ - 理解系统提示词 `_DECOMPOSE_SYSTEM_PROMPT` 如何约束大模型输出。
252
+ - 理解 `decompose()` 如何调用 `get_text()`。
253
+ - 理解 `_parse_json_array()` 为什么要兼容代码块、完整 JSON、夹杂文本的 JSON 以及普通行文本。
254
+
255
+ 这个模块能帮助你理解:实际调用大模型时,不能完全假设输出一定是严格 JSON,所以项目做了较宽松的解析兜底。
256
+
257
+ ### 第 3 步:看并发执行子任务
258
+
259
+ 继续读:
260
+
261
+ 1. `src/worker.py`
262
+
263
+ 阅读目标:
264
+
265
+ - 理解每个子任务如何被包装成 messages。
266
+ - 理解它只负责单个任务,不负责并发。
267
+ - 理解并发是在 `orchestrator.py` 里用 `asyncio.gather` 触发的。
268
+
269
+ 这里要区分两个概念:
270
+
271
+ - `worker.py` 负责“怎么执行一个任务”。
272
+ - `orchestrator.py` 负责“同时执行多个任务”。
273
+
274
+ ### 第 4 步:看中间结果合并和最终总结
275
+
276
+ 继续读:
277
+
278
+ 1. `src/merger.py`
279
+ 2. `src/summarizer.py`
280
+
281
+ 阅读目标:
282
+
283
+ - 理解 `merge_results()` 只是本地字符串拼接,不调用模型。
284
+ - 理解 `summarize()` 会把合并后的中间材料再次交给大模型,让模型生成最终报告。
285
+
286
+ `merger.py` 和 `summarizer.py` 是两个阶段:
287
+
288
+ - `merger.py`:整理原始材料。
289
+ - `summarizer.py`:生成面向用户的最终报告。
290
+
291
+ ### 第 5 步:看报告保存
292
+
293
+ 继续读:
294
+
295
+ 1. `src/reporter.py`
296
+
297
+ 阅读目标:
298
+
299
+ - 理解 `OUTPUT_DIR` 的来源。
300
+ - 理解如何生成安全文件名。
301
+ - 理解 `aiofiles.open` 的异步文件写入方式。
302
+
303
+ 这个模块的职责很单一:把最终 Markdown 内容保存成文件。
304
+
305
+ ### 第 6 步:最后看底层 AI 调用和配置
306
+
307
+ 最后读:
308
+
309
+ 1. `src/config.py`
310
+ 2. `src/ai_tools.py`
311
+
312
+ 阅读目标:
313
+
314
+ - 理解 `.env` 如何加载。
315
+ - 理解 `API_BASE`、`MODEL_NAME`、`API_KEY` 如何被读取。
316
+ - 理解 `ai_tools.py` 如何调用 OpenAI 兼容的 `/chat/completions` 接口。
317
+ - 理解流式响应中 `data: ...` 和 `[DONE]` 的处理逻辑。
318
+
319
+ `ai_tools.py` 是底层基础设施模块。刚开始读项目时可以先知道它“负责调用模型”即可,等理解业务流程后再回头细看。
320
+
321
+ ## 八、异步编程重点
322
+
323
+ 这个项目里最值得学习的异步点有三个:
324
+
325
+ ### 1. 事件循环入口
326
+
327
+ ```python
328
+ if __name__ == "__main__":
329
+ asyncio.run(main())
330
+ ```
331
+
332
+ `asyncio.run()` 会创建事件循环,执行异步入口函数 `main()`,并在结束后关闭事件循环。
333
+
334
+ ### 2. 并发执行多个子任务
335
+
336
+ ```python
337
+ raw_results = await asyncio.gather(
338
+ *[execute_subtask(i + 1, len(subtasks), task) for i, task in enumerate(subtasks)],
339
+ return_exceptions=True,
340
+ )
341
+ ```
342
+
343
+ 这里会把多个子任务同时发起,而不是一个执行完再执行下一个。由于每个子任务主要在等待网络请求结果,所以非常适合用异步并发提升整体速度。
344
+
345
+ `return_exceptions=True` 的作用是:某个子任务失败时,不会让整个 `gather` 直接抛异常中断,而是把异常作为结果返回,后续统一处理。
346
+
347
+ ### 3. 异步网络和异步文件 I/O
348
+
349
+ `src/ai_tools.py` 中使用:
350
+
351
+ ```python
352
+ httpx.AsyncClient(...)
353
+ ```
354
+
355
+ `src/reporter.py` 中使用:
356
+
357
+ ```python
358
+ aiofiles.open(...)
359
+ ```
360
+
361
+ 这两个模块分别展示了异步网络请求和异步文件写入。
362
+
363
+ ## 九、数据流说明
364
+
365
+ 核心数据从用户输入到最终文件,大致经历下面的变化:
366
+
367
+ ```text
368
+ topic: str
369
+ |
370
+ v
371
+ subtasks: list[str]
372
+ |
373
+ v
374
+ results: list[str]
375
+ |
376
+ v
377
+ merged: str
378
+ |
379
+ v
380
+ report: str
381
+ |
382
+ v
383
+ filepath: str
384
+ ```
385
+
386
+ 对应模块:
387
+
388
+ | 数据 | 产生位置 | 说明 |
389
+ | --- | --- | --- |
390
+ | `topic` | `main.py` | 用户输入的原始调研需求 |
391
+ | `subtasks` | `decomposer.py` | 大模型拆解后的子任务列表 |
392
+ | `results` | `worker.py` | 每个子任务的调研结果 |
393
+ | `merged` | `merger.py` | 合并后的 Markdown 中间材料 |
394
+ | `report` | `summarizer.py` | 最终 Markdown 报告 |
395
+ | `filepath` | `reporter.py` | 保存后的文件绝对路径 |
396
+
397
+ ## 十、值得注意的实现细节
398
+
399
+ ### 1. `ai_tools.py` 在导入时检查 API_KEY
400
+
401
+ `src/ai_tools.py` 中有如下逻辑:
402
+
403
+ ```python
404
+ if not API_KEY:
405
+ print("错误: 请在 .env 文件中配置 API_KEY")
406
+ sys.exit(1)
407
+ ```
408
+
409
+ 这意味着只要导入了 `src.ai_tools`,如果没有配置 `API_KEY`,程序就会直接退出。由于 `decomposer.py`、`worker.py`、`summarizer.py` 都会导入 `get_text`,所以这些模块也间接受这个检查影响。
410
+
411
+ ### 2. `decomposer.py` 对模型输出做了容错
412
+
413
+ 模型可能输出:
414
+
415
+ - 标准 JSON 数组。
416
+ - 包在 Markdown 代码块中的 JSON。
417
+ - 文本中夹带 JSON 数组。
418
+ - 多行文本列表。
419
+
420
+ `_parse_json_array()` 按上述顺序尝试解析,最后才退化成普通行文本列表。
421
+
422
+ ### 3. `merger.py` 中有一个轻微冗余判断
423
+
424
+ `orchestrator.py` 已经把异常转换成了字符串:
425
+
426
+ ```python
427
+ if isinstance(r, BaseException):
428
+ results.append(f"[执行失败] {r}")
429
+ ```
430
+
431
+ 因此 `merge_results()` 里的这段判断通常不会再命中:
432
+
433
+ ```python
434
+ if isinstance(result, Exception):
435
+ lines.append(f"该子任务执行失败: {result}\n")
436
+ ```
437
+
438
+ 这不影响运行,只是从代码整洁度上看略有重复。
439
+
440
+ ### 4. 任务分解数量依赖提示词约束
441
+
442
+ 项目通过 prompt 要求模型将任务拆成 3-5 个子任务,但代码本身没有强制限制数量。如果模型返回更多或更少子任务,程序仍会照常执行。
443
+
444
+ ### 5. 目前没有重试机制
445
+
446
+ 如果模型接口偶发失败,`execute_subtask()` 的异常会被 `asyncio.gather(..., return_exceptions=True)` 收集并写入结果。但 `decompose()` 和 `summarize()` 失败时会直接中断流程,因为它们不在类似的异常兜底里。
447
+
448
+ ## 十一、适合练习和扩展的方向
449
+
450
+ 如果你想通过这个项目继续练习,可以按下面顺序扩展:
451
+
452
+ 1. 给 `ai_tools.py` 增加请求失败重试机制。
453
+ 2. 给 `decompose()` 增加子任务数量上限和空结果兜底。
454
+ 3. 为 `get_text()` 增加非流式调用模式。
455
+ 4. 把执行过程中的 token、耗时、失败原因记录到日志。
456
+ 5. 增加命令行参数,例如 `--topic`、`--output-dir`、`--model`。
457
+ 6. 增加单元测试,优先测试 `_parse_json_array()` 和 `merge_results()`。
458
+ 7. 将多个 worker 的并发数量限制为固定值,避免一次性创建过多请求。
459
+
460
+ ## 十二、最短学习路线
461
+
462
+ 如果时间有限,建议按这个顺序快速阅读:
463
+
464
+ ```text
465
+ 1. src/main.py
466
+ 2. src/orchestrator.py
467
+ 3. src/decomposer.py
468
+ 4. src/worker.py
469
+ 5. src/merger.py
470
+ 6. src/summarizer.py
471
+ 7. src/reporter.py
472
+ 8. src/config.py
473
+ 9. src/ai_tools.py
474
+ ```
475
+
476
+ 其中最关键的三个文件是:
477
+
478
+ ```text
479
+ src/orchestrator.py
480
+ src/decomposer.py
481
+ src/ai_tools.py
482
+ ```
483
+
484
+ 读懂这三个文件后,就基本掌握了项目的主流程、任务分解逻辑和底层模型调用方式。