16-day4-鲁棒性-外挂工具协作_实验3
📚 16-day4-鲁棒性-外挂工具协作_实验3
本教程面向开发者或技术使用者,
说明如何将一个独立的 Python 占卜程序作为“工具”接入智能体(Agent),
实现自然语言驱动的自动占卜功能。
一、项目目标
- 将原有的命令行金钱卦程序(
money_divination/main.py)改造为 可被智能体调用的函数模块 - 在 LangChain Agent 中注册该功能为
@tool - 用户只需用自然语言提问(如“我该不该辞职?”),Agent 自动调用占卜工具并返回完整卦象分析
二、目录结构说明
你的项目采用模块化设计,结构如下:
your_project/
├── robust_agent_3.py ← 主智能体程序(入口)
└── money_divination/ ← 外置占卜程序(作为独立模块)
├── core/
│ ├── __init__.py ← 使 core 成为 Python 包
│ ├── coin_toss.py ← 模拟掷铜钱
│ ├── parse_gua.py ← 解析爻线生成卦象
│ ├── lookup.py ← 查询卦辞与爻辞(读取 CSV)
└── └── divination_tool.py ← ✨ 新增:纯函数接口,供 Agent 调用
├── data/
│ ├── hexagrams.csv ← 六十四卦数据
│ └── yao_dict.csv ← 爻辞数据
└── main.py ← 原始命令行版本(保留,可独立运行)
✅ 关键点:
divination_tool.py 是连接两个系统的桥梁——它不包含任何交互逻辑,只提供 perform_divination(question: str) -> str 函数。
三、核心改造步骤
步骤 1:创建纯函数接口(divination_tool.py)
# money_divination/core/divination_tool.py
from datetime import datetime
from .coin_toss import toss_six_times_with_text
from .parse_gua import parse_gua
from .lookup import get_hexagram_meaning, get_all_moving_yao_texts
def perform_divination(question: str) -> str:
"""执行一次完整占卜,返回格式化结果字符串(无副作用)"""
# ...(内部逻辑:摇卦 → 解析 → 查询 → 拼接结果)
return "\n".join(output_lines)
🔒 该函数:
- 不使用
input()、print()、pyperclip- 不依赖全局状态
- 输入仅为问题字符串,输出仅为结果字符串
- 完全可测试、可复用、可被 Agent 安全调用
步骤 2:在智能体中注册为工具(robust_agent_3.py)
# 导入外置模块
from money_divination.core.divination_tool import perform_divination
@tool
def divination(query: str):
"""
使用《周易》金钱卦进行占卜。
示例:divination("我今年财运如何?")
"""
try:
return perform_divination(query)
except Exception as e:
return f"占卜失败:{str(e)}"
✅ 注意:
- 工具名
divination会出现在 Agent 的可用工具列表中- Docstring 是 Agent 决定是否调用此工具的关键依据
步骤 3:加入工具列表并运行
tools = [get_current_time, divide, is_leap_year, search_web, divination]
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, ...)
完整示例
pip install pyperclip
cd day4
wget http://10.7.164.5/money_divination.tar .
tar -xf money_divination.tar
tee robust_agent_3.py <<'EOF'
# robust_agent_3.py
import os
from datetime import datetime
from dotenv import load_dotenv
from langchain_core.tools import tool
from langchain.agents import AgentExecutor, create_react_agent
from langchain import hub
from langchain_openai import ChatOpenAI
from money_divination.core.divination_tool import perform_divination
# 加载 API Key
load_dotenv()
@tool
def get_current_time(dummy: str = ""):
"""获取当前日期和时间"""
return datetime.now().strftime("%Y年%m月%d日 %H:%M")
@tool
def divide(a_str: str) -> str:
"""
安全除法计算:输入格式为 'a,b',返回 a / b 的结果。
示例输入: "10,3" → 输出: "3.333..."
"""
try:
parts = a_str.split(",")
if len(parts) != 2:
return "错误:请输入两个数字,用逗号分隔,如 '10,2'"
a = float(parts[0].strip())
b = float(parts[1].strip())
if b == 0:
return "错误:除数不能为零!"
result = a / b
return str(result)
except ValueError:
return "错误:请输入有效的数字"
except Exception as e:
return f"计算失败: {str(e)}"
@tool
def is_leap_year(year_str: str):
"""
判断指定年份是否为闰年。
输入:年份(字符串),如 "2024"
输出:"2024年是闰年" 或 "2023年不是闰年"
"""
try:
year = int(year_str.strip())
is_leap = (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
if is_leap:
return f"{year}年是闰年"
else:
return f"{year}年不是闰年"
except ValueError:
return "错误:请输入有效的年份(如 2024)"
except Exception as e:
return f"判断失败: {str(e)}"
@tool
def search_web(query: str):
"""
模拟网页搜索(仅返回固定摘要,避免真实爬虫问题)
实际项目应替换为合法搜索引擎 API(如 SerpAPI)
"""
return f"(模拟)关于 '{query}' 的搜索结果摘要:这是一个演示响应。"
@tool
def divination(query: str):
"""
使用《周易》金钱卦进行占卜。
输入:用户想问的问题(字符串)
输出:完整的占卜结果(包含卦象、卦辞、爻辞等)
示例:divination("我今年能升职吗?")
"""
try:
return perform_divination(query)
except Exception as e:
return f"占卜失败:{str(e)}"
# 初始化 Qwen 模型
llm = ChatOpenAI(
model="qwen-max",
temperature=0,
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=os.getenv("DASHSCOPE_API_KEY")
)
# 注册所有工具
tools = [get_current_time, divide, is_leap_year, search_web, divination]
# 加载 ReAct 提示模板
prompt = hub.pull("hwchase17/react")
# 创建 Agent
agent = create_react_agent(llm, tools, prompt)
# 创建执行器
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
max_iterations=6
)
# 交互式主循环
print("🛡️ Day 4:鲁棒智能体(多工具 + 错误处理)")
print("尝试提问:")
print("- 计算 100 除以 0")
print("- 2024年是闰年吗?")
print("- 现在几点?再算一下 2024 除以 4")
print("- 工具里面有一个周易,可以问建议")
print("- 输入 quit 退出\n")
while True:
try:
user_input = input("💬 问题: ").strip()
if not user_input:
continue
if user_input.lower() in {"quit", "exit", "q"}:
print("👋 再见!")
break
result = agent_executor.invoke({"input": user_input})
print("\n" + "=" * 60)
print("🤖 回答:")
print(result["output"])
print()
except KeyboardInterrupt:
print("\n👋 被中断,再见!")
break
except Exception as e:
print(f"❌ 系统错误: {e}\n")
EOF
四、使用示例
python robust_agent_3.py
用户输入:
💬 问题: 我该接受这份新工作 offer 吗?
Agent 行为:
- 分析问题语义,识别出涉及“决策”“未来”“建议”
- 决定调用
divination工具 - 执行
perform_divination("我该接受这份新工作 offer 吗?") - 返回完整卦象(含本卦、之卦、动爻、卦辞、爻辞)
输出片段示例:
🔮 金钱卦占卜结果 (2026年01月28日 22:15)
所问之事:我该接受这份新工作 offer 吗?
本卦(现状):水火既济
之卦(趋势):地火明夷
动爻:[3]
📜 卦辞(水火既济):
既济:亨小,利贞;初吉终乱。
⚡ 动爻爻辞:
第3爻:高宗伐鬼方,三年克之,小人勿用。
💡 提示:有动爻,动爻表示变化的关键,需特别关注。
五、优势与设计理念
| 特性 | 说明 |
|---|---|
| 解耦设计 | 占卜逻辑完全独立于智能体,便于单独测试或替换 |
| 可维护性 | 修改数据(CSV)无需改动 Agent 代码 |
| 安全性 | 无网络请求、无文件写入、无用户输入阻塞 |
| 扩展性 | 未来可轻松添加其他工具 |
六、常见问题(FAQ)
Q1:为什么不能直接调用 main.py?
A:
main.py包含input()和print(),会在 Agent 调用时卡住或产生混乱输出。必须改造成无副作用的纯函数。
Q2:如果占卜模块路径变了怎么办?
A:只要保持
money_divination/在项目根目录,并从根目录运行脚本,Python 就能自动找到它。不要随意移动目录。
Q3:能否让 Agent 自动判断何时需要占卜?
A:是的!关键在于
@tool的 docstring 描述。你可以优化描述,例如:"""当用户询问关于人生决策、运势、未来趋势等问题时,调用此工具进行《周易》占卜。"""
七、后续优化建议
- ✅ 添加单元测试(
test_divination_tool.py) - ✅ 支持自定义铜钱正反面规则(通过参数)
- ✅ 将结果转为 JSON 格式,便于前端展示
- ✅ 集成真实搜索引擎(替换
search_web)
🌟 总结:
通过“外置模块 + 纯函数接口 + LangChain Tool”,将传统文化智慧融入现代 AI 智能体,实现了 “问事即占,有分析过程,有简明扼要的解释” 的体验!
📄 附:运行前提
- Python ≥ 3.9
- 安装依赖:
pip install langchain langchain-openai python-dotenv pyperclip - 设置环境变量:
DASHSCOPE_API_KEY=your_key(在.env文件中)
浙公网安备 33010602011771号