AI Agent 实习生工作日记 — Day 4(技术补充:agent_engine.py 底层实现)

1. 架构总览:agent_engine.py 在 Social Media Agent 中的职责

在我们的 social-media-agent 项目中,tools/x_tools.py 是纯粹的"被动函数库",而 agent_engine.py 才是整个系统的调度中枢(Brain & Pipeline)。

它负责三件事:

  1. 记忆维护(Memory Management) :维护一个全局或 Session 级别的 messages 列表,存储所有的 User 提问、Thought 思考、Action 指令以及 Observation 返回值。

  2. 工具映射(Tool Dispatching) :把大模型输出的字符串函数名(如 "fetch_tweet_comments"),映射并执行 x_tools.py 中真正的 Python 函数。

  3. ReAct 死循环拦截与收敛 :控制单次问题的最大思考轮数(如 max_turns=5),防止 Agent 陷入死循环暴刷 X API。

2. 核心代码解析:agent_engine.py 的完整实现

在 Alex 导师的指导下,我手写并调通了适用于本系统的 agent_engine.py:

复制代码
import os
import json
from typing import List, Dict, Any
from openai import OpenAI

# 1. 导入昨天与今天在 tools/x_tools.py 中写好的工具
from tools.x_tools import fetch_x_user_recent_posts, fetch_tweet_comments

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# 2. 注册可用的工具函数字典
TOOL_REGISTRY = {
    "fetch_x_user_recent_posts": fetch_x_user_recent_posts,
    "fetch_tweet_comments": fetch_tweet_comments
}

# 3. 严格的 System Prompt 设定:注入多轮记忆与按次计费(Pay-per-use)的省钱规则
SYSTEM_PROMPT = """
你是一个顶级的 Social Media 智能分析 Agent,支持连接 X (Twitter) API 实时获取数据。

你必须严格遵循 ReAct (Thought -> Action -> Action Input -> Observation) 范式回答问题。

可用工具列表:
1. fetch_x_user_recent_posts(username: str, max_results: int = 5)
   - 用途:查询某 X 用户的 ID、基本信息以及最近发布的原创推文。
2. fetch_tweet_comments(tweet_id: str, max_comments: int = 10)
   - 用途:根据具体推文的 tweet_id,查询其下方的真实用户评论列表。

极重要规则(成本与记忆控制):
- 注意:X API 为按次计费模式!如果历史对话 (Memory) 中已经包含你需要的 tweet_id 或用户信息,直接从历史 Observation 中提取,严禁重复调用 fetch_x_user_recent_posts!
- 当需要分析某条推文的评论时,必须先检查历史消息里是否有对应的 tweet_id,将该 tweet_id 作为参数传给 fetch_tweet_comments。

输出格式规范:
Thought: 分析当前用户意图与历史上下文,说明下一步需要什么信息。
Action: 工具名称 (必须是上述工具之一)
Action Input: 工具参数的 JSON 字典,例如 {"username": "elonmusk"}
Observation: [由系统自动插入工具运行结果,你无需手动填写]

当获取到足够信息后,请输出:
Thought: 我已经获得了足够的信息。
Final Answer: 给出最终的总结回答。
"""

class SocialMediaAgentEngine:
    def __init__(self):
        # 初始化短期记忆上下文列表 (Short-term Memory Context)
        self.conversation_history: List[Dict[str, str]] = [
            {"role": "system", "content": SYSTEM_PROMPT}
        ]

    def chat(self, user_input: str, max_turns: int = 5) -> str:
        """接收用户输入,发起多轮 ReAct 循环推理"""
        # 将用户最新提问追加到历史 Memory 中
        self.conversation_history.append({"role": "user", "content": f"Question: {user_input}"})
        
        print(f"\n📥 收到用户输入: '{user_input}'")
        
        for turn in range(max_turns):
            print(f"--- 🔄 ReAct 循环第 {turn + 1} 轮 ---")
            
            # 调用 LLM 进行推理
            response = client.chat.completions.create(
                model="gpt-4o",
                messages=self.conversation_history,
                temperature=0.1,  # 低随机度保证工具调用的稳定性
                stop=["Observation:"] # 强制拦截,避免 LLM 伪造 Observation
            )
            
            llm_text = response.choices[0].message.content.strip()
            print(f"🧠 模型思考与决策:\n{llm_text}")
            
            # 1. 存入 Memory:将模型的 Thought/Action 追加到对话历史
            self.conversation_history.append({"role": "assistant", "content": llm_text})
            
            # 2. 终止条件检查:如果生成了 Final Answer,返回答案并结束本次交互
            if "Final Answer:" in llm_text:
                final_answer = llm_text.split("Final Answer:")[-1].strip()
                return final_answer
                
            # 3. 解析 Action 并匹配工具执行
            if "Action:" in llm_text and "Action Input:" in llm_text:
                try:
                    # 提取 Action 与 Action Input
                    lines = llm_text.split("\n")
                    action_line = [l for l in lines if l.startswith("Action:")][0]
                    input_line = [l for l in lines if l.startswith("Action Input:")][0]
                    
                    tool_name = action_line.replace("Action:", "").strip()
                    tool_args = json.loads(input_line.replace("Action Input:", "").strip())
                    
                    # 校验工具是否存在并调用 tools/x_tools.py 中的实现
                    if tool_name in TOOL_REGISTRY:
                        print(f"⚙️ 正在分发执行工具 [{tool_name}],参数: {tool_args}...")
                        exec_result = TOOL_REGISTRY[tool_name](**tool_args)
                    else:
                        exec_result = {"status": "error", "message": f"未找到名为 {tool_name} 的工具"}
                        
                    # 4. 组装 Observation 结果,喂回给 Memory
                    obs_str = f"Observation: {json.dumps(exec_result, ensure_ascii=False)}"
                    print(f"📡 工具返回 Observation 存入 Memory: {obs_str[:150]}...")
                    
                    # 关键步骤:把工具运行的真实数据写入对话历史,作为下一轮推理的依据!
                    self.conversation_history.append({"role": "user", "content": obs_str})
                    
                except Exception as e:
                    error_obs = f"Observation: 解析或执行工具出错: {str(e)}"
                    self.conversation_history.append({"role": "user", "content": error_obs})
            else:
                # 如果既没有 Final Answer 也没有标准 Action,提示格式错误
                self.conversation_history.append({
                    "role": "user", 
                    "content": "Observation: 输出格式不符合要求,请确保输出 Action 和 Action Input,或给出 Final Answer。"
                })
                
        return "很抱歉,在限定的思考轮次内未能在 X 平台上完成分析。"
3. 链路闭环演示:Memory 如何跨轮次传递数据?

为了深刻理解 agent_engine.py 如何与 x_tools.py 协同工作,Alex 带我推演了当用户连续问第二句时,内存数据(self.conversation_history)的具体形态:

复制代码
self.conversation_history 存储的完整上下文队列:

[消息 1] System Prompt (包含全局规则与工具列表)
[消息 2] User: "Question: 埃隆·马斯克(@elonmusk)最新的一条 X 说了啥?"
[消息 3] Assistant: "Thought: ... Action: fetch_x_user_recent_posts Action Input: {\"username\": \"elonmusk\"}"
[消息 4] User (Observation): "Observation: {\"status\": \"success\", \"tweets\": [{\"tweet_id\": \"1846001122334455667\", ...}]}"
[消息 5] Assistant: "Thought: ... Final Answer: 马斯克最新发布了星舰试飞成功的消息..."

--- 此时发起第二句提问 ---

[消息 6] User: "Question: 针对这一条,评论看用户的 sentiment 怎么样?"

当 agent_engine.py 将这一整串 conversation_history 扔给大模型时:

  1. 大模型看到了 [消息 6] 的代词"这一条"。

  2. 大模型往上检索,在 [消息 4] 的 Observation 字符串里抓到了 tweet_id: 1846001122334455667。

  3. 大模型决定生成 Action: fetch_tweet_comments + Action Input: {"tweet_id": "1846001122334455667"}。

  4. agent_engine.py 解析该字符串,自动在 TOOL_REGISTRY 中匹配并触发 tools/x_tools.py 中的 fetch_tweet_comments() 函数!

相关推荐
老金带你玩AI1 小时前
DeepSeek升级了:从故宫3D到赛车游戏,价格没涨
人工智能
lsswear1 小时前
Agent 框架新手入门与实战部署指南
大数据·人工智能·算法
电子元件小说家2 小时前
2026年AI与汽车电子双轮驱动下,大感值贴片功率电感选型与替代观察
人工智能·汽车
七牛开发者2 小时前
从 Muse、dots 和 Grok Bot 看 Personal Agent:状态、记忆、授权与后台执行
人工智能
一枕槐安4872 小时前
AG 幻觉率从 24% 压到 4%:我把召回评估做成了流水线
人工智能·ai
北方的银狐-Zero2 小时前
OntoL本体产品生产案例分析-项目,合同,付款,本体交付案例
人工智能·本体论
二川bro2 小时前
wiki编译拆解
人工智能
迅为电子2 小时前
【迅为RK3588开发板与RK1828算力卡方案】工业异常检测:缺陷定位全流程演示
人工智能