ReAct Agent实战:用Python+Qwen-Turbo构建维基百科查询助手

最近在开发一个需要实时获取百科知识的项目时,我发现直接调用现成的知识图谱API不仅成本高,而且灵活性不足。于是尝试用ReAct范式结合Qwen-Turbo模型,自己搭建了一个维基百科查询工具。这个方案最吸引我的地方在于,它完美结合了大语言模型的推理能力和传统API的数据可靠性。

1. ReAct范式核心设计

ReAct(Reasoning+Acting)的核心价值在于建立了"思考-行动-观察"的闭环机制。与传统的单次查询不同,这种范式允许AI系统像人类一样进行多轮验证和调整。

1.1 动作指令设计

在维基百科查询场景中,我们定义了三种基础动作:

ACTION_TYPES = {
    "Search": "在维基百科搜索指定实体",
    "Lookup": "在当前页面查找关键词",
    "Finish": "返回最终答案"
}

每种动作都需要处理不同的边界情况:

  • Search:需处理无结果、模糊匹配、页面重定向等情况
  • Lookup:需维护查找状态(如当前结果索引)
  • Finish:需验证答案是否符合问题要求

1.2 状态管理机制

由于ReAct是多轮交互过程,必须妥善管理以下状态:

状态类型 存储内容 生命周期
Session State 当前查询页面内容 单次查询全程
Lookup State 关键词匹配结果及索引 每次Lookup动作期间
History State 完整的Thought-Action轨迹 用于调试和优化

2. 工程实现关键点

2.1 Wikipedia API处理技巧

维基百科的API响应需要特殊处理:

def clean_wiki_text(text):
    """处理维基百科返回的特殊字符和格式"""
    text = re.sub(r'\[\d+\]', '', text)  # 去除引用标记
    text = text.replace('\xa0', ' ')     # 处理特殊空格
    return text.strip()

常见异常情况处理方案:

  1. 速率限制:实现指数退避重试机制
  2. 页面重定向:自动跟随301/302跳转
  3. 消歧义页:识别"may refer to"提示并建议精确查询

2.2 页面解析优化

使用BeautifulSoup时,我发现这些技巧能显著提升解析效果:

  • 优先提取<p>标签中的前三个段落
  • 忽略表格和引用框内容(通常非正文)
  • 对数学公式等特殊内容进行降级处理
def extract_main_content(html):
    soup = BeautifulSoup(html, 'html.parser')
    # 优先获取导言部分
    intro = soup.find_all('p', class_=None)
    content = [p.get_text() for p in intro[:3] if len(p.get_text().split()) > 10]
    return ' '.join(content)

3. Qwen-Turbo适配策略

3.1 提示词工程

要让国产模型准确理解ReAct指令,提示词需要特别设计:

请严格按照以下格式响应:
Thought [数字]: 分析当前情况并决定下一步
Action [数字]: Search[查询词]|Lookup[关键词]|Finish[答案]

示例:
Thought 1: 需要先确定量子计算的基本概念
Action 1: Search[量子计算]

关键改进点:

  • 在示例中强调动作格式
  • 限制输出长度避免冗余
  • 明确禁止自由发挥

3.2 模型微调技巧

针对Qwen-Turbo的特点,我总结了这些优化方法:

  1. 温度参数:设置为0.3-0.5平衡创造性和稳定性
  2. 停止标记:配置["\nObservation", "\nThought"]防止跑题
  3. 重试机制:当响应不符合格式时自动重新生成

4. 完整实现方案

4.1 系统架构

graph TD
    A[用户问题] --> B(ReAct引擎)
    B --> C{动作判断}
    C -->|Search| D[维基百科API]
    C -->|Lookup| E[页面解析器]
    C -->|Finish| F[结果验证]
    D --> G[返回页面内容]
    E --> H[返回匹配句子]
    F --> I[输出最终答案]

4.2 核心代码结构

class WikipediaAgent:
    def __init__(self, model="qwen-turbo"):
        self.model = model
        self.session_page = None
        self.lookup_state = {}
        
    def search(self, entity: str) -> tuple:
        """处理搜索请求"""
        # 实现搜索逻辑
    
    def lookup(self, keyword: str) -> str:
        """处理页面内查询"""
        # 实现查找逻辑
    
    def run(self, question: str) -> dict:
        """执行完整ReAct流程"""
        trajectory = []
        for step in range(1, MAX_STEPS+1):
            thought, action = self._generate_thought_action(question, trajectory)
            if action.startswith("Finish"):
                return self._process_finish(action)
            # 处理其他动作...

4.3 性能优化建议

根据实际测试数据(QPS=5的情况下):

优化措施 平均响应时间 准确率提升
页面缓存 2.1s → 1.4s +5%
并行查询 1.4s → 0.9s -
结果预取 0.9s → 0.7s +8%

5. 实战问题排查

在项目落地过程中,这几个问题最值得注意:

  1. 编码问题:维基百科返回的文本可能混合多种编码,需要统一转换
  2. 会话保持:长时间对话时需要定期清理历史记录避免prompt过长
  3. 结果验证:对Finish动作的答案进行可信度评估

提示:开发时可以添加debug模式,完整记录每个步骤的输入输出,这对优化提示词非常有帮助。

我在实际项目中遇到过一个典型问题:当查询"Python"时,模型会陷入动物和编程语言的歧义中无法自拔。后来通过在Search动作中强制添加限定词(如"Python programming")解决了这个问题。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐