fix: 修复 NameError/状态污染/类型标注/统计; 补全练习与 main; 新增 config/.gitignore/requirements; 文档统一
This commit is contained in:
@@ -200,30 +200,28 @@ class MultiAgentSystem:
|
||||
|
||||
def __init__(self):
|
||||
# 注册各个 Agent
|
||||
self.agents = {
|
||||
"generator": GeneratorAgent(),
|
||||
"validator": ValidatorAgent(),
|
||||
"searcher": SearcherAgent(),
|
||||
}
|
||||
# 协调器
|
||||
self.orchestrator = Orchestrator(self.agents)
|
||||
self.agents: dict[str, Agent] = {}
|
||||
|
||||
def process(self, requirement: str) -> str:
|
||||
"""协调多个 Agent 处理请求"""
|
||||
def register(self, agent: Agent) -> None:
|
||||
self.agents[agent.name] = agent
|
||||
|
||||
def process(self, requirement: str):
|
||||
# 1. 搜索相关知识
|
||||
context = self.agents["searcher"].search(requirement)
|
||||
searcher = self.agents.get("searcher")
|
||||
context = searcher.process(requirement) if searcher else ""
|
||||
|
||||
# 2. 生成(可能需要多轮)
|
||||
for attempt in range(3):
|
||||
draft = self.agents["generator"].generate(requirement, context)
|
||||
generator = self.agents.get("generator")
|
||||
draft = generator.process({"requirement": requirement, "context": context}) if generator else requirement
|
||||
|
||||
# 3. 验证
|
||||
validation = self.agents["validator"].validate(draft)
|
||||
# 3. 验证
|
||||
validator = self.agents.get("validator")
|
||||
if validator:
|
||||
validation = validator.process(draft)
|
||||
if not validation.get("passed", True):
|
||||
return {"error": "验证失败", "validation": validation}
|
||||
|
||||
if validation["passed"]:
|
||||
return validation["result"]
|
||||
|
||||
return "处理失败"
|
||||
return draft
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -4,6 +4,9 @@ Step 05-07: RAG / Self-Correction / Multi-Agent
|
||||
进阶内容代码示例
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════════════
|
||||
# RAG 实现
|
||||
# ═══════════════════════════════════════════════════════════════════════════════════════
|
||||
@@ -152,9 +155,6 @@ class SelfCorrectingAgent:
|
||||
# Multi-Agent 实现
|
||||
# ═══════════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Callable
|
||||
|
||||
|
||||
@dataclass
|
||||
class AgentMessage:
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
Step 05-07 练习题:进阶能力
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🎯 练习目标:
|
||||
1. 体验 RAG 的检索质量
|
||||
2. 写一个 Self-Correction 闭环
|
||||
3. 设计多 Agent 编排
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
"""
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 练习 1:提升 SimpleRAG 的检索质量
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
"""
|
||||
任务:
|
||||
SimpleRAG.retrieve() 当前用 Jaccard 相似度 + 简单分词。
|
||||
改造为:把分词改成 "中文按字 + 英文按词 + 大小写归一化" 后再算 Jaccard。
|
||||
|
||||
要求:
|
||||
1. 复用 SimpleRAG 类,不要重写
|
||||
2. 实现 upgrade_retrieve(rag) 替换 rag.retrieve 方法
|
||||
3. 用一个含中英文的小语料验证
|
||||
|
||||
提示:
|
||||
- re.findall(r'[\u4e00-\u9fff]|[A-Za-z]+', text.lower())
|
||||
"""
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 练习 2:实现 Self-Correction 主循环
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
"""
|
||||
任务:
|
||||
SelfCorrectingAgent 在 concept.py 中是骨架。请补全它的 run() 方法:
|
||||
|
||||
def run(self, requirement: str, generate_fn, validate_fn, max_retries=3):
|
||||
for attempt in range(max_retries):
|
||||
output = generate_fn(requirement, attempt, feedback)
|
||||
validation = validate_fn(output)
|
||||
if validation.passed:
|
||||
return output
|
||||
feedback = self.build_feedback(validation)
|
||||
return output
|
||||
|
||||
要求:
|
||||
1. 第一次 attempt 不带 feedback
|
||||
2. 每次失败用 build_feedback 拼出新的 feedback
|
||||
3. 超过 max_retries 返回最后一次 output(不要抛异常)
|
||||
"""
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 练习 3:给 MultiAgentSystem 加超时与失败回退
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
"""
|
||||
任务:
|
||||
MultiAgentSystem.process() 是顺序调用 searcher -> generator -> validator。
|
||||
任何一个 Agent 抛异常都让整个流程崩。
|
||||
|
||||
要求:
|
||||
1. 在 process() 外层包 try/except,失败时返回 {"error": str(e)}
|
||||
2. 给每个 Agent 加 timeout_seconds 参数(用 time.monotonic)
|
||||
3. 验证:故意让 validator 抛异常,确认 process() 不会让程序崩溃
|
||||
|
||||
提示:
|
||||
- time.monotonic() 不受系统时间影响
|
||||
- 简单演示里可以靠 sleep + 时间比较实现超时
|
||||
"""
|
||||
|
||||
|
||||
def test_exercises():
|
||||
from step_05_07_advanced.concept import SimpleRAG
|
||||
|
||||
rag = SimpleRAG()
|
||||
rag.add_document("JasperReports 是一个 Java 报表库", {"source": "doc1"})
|
||||
rag.add_document("JRXML 是 JasperReports 模板格式", {"source": "doc2"})
|
||||
print(rag.retrieve("JasperReports"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_exercises()
|
||||
@@ -0,0 +1,169 @@
|
||||
"""
|
||||
Step 05-07 练习题答案
|
||||
|
||||
⚠️ 先自己思考,再看答案!
|
||||
⚠️ 答案不是唯一的,这里只是其中一种实现
|
||||
"""
|
||||
|
||||
import re
|
||||
import time
|
||||
from typing import Callable
|
||||
|
||||
from step_05_07_advanced.concept import (
|
||||
Agent,
|
||||
MultiAgentSystem,
|
||||
SelfCorrectingAgent,
|
||||
SimpleRAG,
|
||||
ValidationResult,
|
||||
)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 练习 1 答案:升级 SimpleRAG 分词
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
_TOKEN_PATTERN = re.compile(r"[\u4e00-\u9fff]|[A-Za-z]+")
|
||||
|
||||
|
||||
def _tokenize(text: str) -> set[str]:
|
||||
return set(_TOKEN_PATTERN.findall(text.lower()))
|
||||
|
||||
|
||||
def upgrade_retrieve(rag: SimpleRAG) -> None:
|
||||
def retrieve(self, query: str, top_k: int = 3):
|
||||
q_words = _tokenize(query)
|
||||
scored = []
|
||||
for doc in self.documents:
|
||||
d_words = _tokenize(doc["text"])
|
||||
union = q_words | d_words
|
||||
if not union:
|
||||
continue
|
||||
score = len(q_words & d_words) / len(union)
|
||||
scored.append((score, doc))
|
||||
scored.sort(key=lambda x: x[0], reverse=True)
|
||||
return [doc for _, doc in scored[:top_k]]
|
||||
|
||||
SimpleRAG.retrieve = retrieve
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 练习 2 答案:Self-Correction 主循环
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def install_self_correction_run() -> None:
|
||||
def run(
|
||||
self: SelfCorrectingAgent,
|
||||
requirement: str,
|
||||
generate_fn: Callable,
|
||||
validate_fn: Callable,
|
||||
max_retries: int = 3,
|
||||
):
|
||||
feedback = None
|
||||
output = None
|
||||
for attempt in range(max_retries):
|
||||
output = generate_fn(requirement, attempt, feedback)
|
||||
validation: ValidationResult = validate_fn(output)
|
||||
if validation.passed:
|
||||
return output
|
||||
feedback = self.build_feedback(validation, output, attempt)
|
||||
return output
|
||||
|
||||
SelfCorrectingAgent.run = run
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 练习 3 答案:MultiAgentSystem 超时与回退
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def install_safe_process() -> None:
|
||||
def process(self: MultiAgentSystem, requirement: str, timeout_seconds: float = 2.0):
|
||||
try:
|
||||
return self._timed_process(requirement, timeout_seconds)
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
def _timed_process(self, requirement: str, timeout_seconds: float):
|
||||
deadline = time.monotonic() + timeout_seconds
|
||||
searcher = self.agents.get("searcher")
|
||||
if searcher:
|
||||
self._check_timeout(deadline)
|
||||
context = searcher.process(requirement)
|
||||
else:
|
||||
context = ""
|
||||
|
||||
generator = self.agents.get("generator")
|
||||
if generator:
|
||||
self._check_timeout(deadline)
|
||||
result = generator.process({"requirement": requirement, "context": context})
|
||||
else:
|
||||
result = requirement
|
||||
|
||||
validator = self.agents.get("validator")
|
||||
if validator:
|
||||
self._check_timeout(deadline)
|
||||
validation = validator.process(result)
|
||||
if not validation.get("passed", True):
|
||||
return {"error": "验证失败", "validation": validation}
|
||||
|
||||
return result
|
||||
|
||||
def _check_timeout(self, deadline: float):
|
||||
if time.monotonic() > deadline:
|
||||
raise TimeoutError("Multi-Agent 处理超时")
|
||||
|
||||
MultiAgentSystem.process = process
|
||||
MultiAgentSystem._timed_process = _timed_process
|
||||
MultiAgentSystem._check_timeout = _check_timeout
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 测试
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
def test_answers():
|
||||
print("\n" + "=" * 60)
|
||||
print("Step 05-07 练习答案测试")
|
||||
print("=" * 60)
|
||||
|
||||
print("\n📝 练习 1: 升级 SimpleRAG")
|
||||
rag = SimpleRAG()
|
||||
rag.add_document("JasperReports 是一个 Java 报表库", {"source": "doc1"})
|
||||
rag.add_document("JRXML 是 JasperReports 模板格式", {"source": "doc2"})
|
||||
upgrade_retrieve(rag)
|
||||
hits = rag.retrieve("JasperReports")
|
||||
print(f" 检索命中 {len(hits)} 条")
|
||||
for d in hits:
|
||||
print(f" - {d['text']}")
|
||||
|
||||
print("\n📝 练习 2: Self-Correction run()")
|
||||
install_self_correction_run()
|
||||
sc = SelfCorrectingAgent()
|
||||
|
||||
def fake_generate(req, attempt, feedback):
|
||||
# 第一次失败,第二次成功
|
||||
return f"v{attempt}"
|
||||
|
||||
def fake_validate(output):
|
||||
passed = output == "v1"
|
||||
return ValidationResult(passed=passed, score=1.0 if passed else 0.2, issues=[] if passed else ["不达标"])
|
||||
|
||||
final = sc.run("测试", fake_generate, fake_validate, max_retries=3)
|
||||
print(f" 最终结果 = {final}")
|
||||
|
||||
print("\n📝 练习 3: Multi-Agent 安全 process()")
|
||||
install_safe_process()
|
||||
|
||||
class BoomValidator(Agent):
|
||||
name = "validator"
|
||||
|
||||
def process(self, input_data):
|
||||
raise RuntimeError("故意崩溃")
|
||||
|
||||
sys = MultiAgentSystem()
|
||||
sys.agents["validator"] = BoomValidator()
|
||||
res = sys.process("任何需求")
|
||||
print(f" 异常被吞掉: {res}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_answers()
|
||||
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Step 05-07: RAG / Self-Correction / Multi-Agent 主程序
|
||||
|
||||
运行方式:
|
||||
cd step_05_07_advanced
|
||||
python main.py
|
||||
"""
|
||||
|
||||
from concept import demo
|
||||
|
||||
|
||||
def main():
|
||||
demo()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user