欢迎来到啾啾的博客🐱。
记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。
有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。
1 引言
让我们结合前两篇的理论与实践,尝试系统性、结构化、全面地分析Agent。
因继续写下去单个文件太长了,本篇代码较上篇做了结构改动,代码放在https://github.com/tataCrayon/LLM-DEV-COOKBOOK仓库。
2 使用工具分析Agent:”日志“
我们之前通过verbose=True的日志,对Agent的行为进行了定性分析(Qualitative Analysis)。
但verbose=True的日志结构还是不够清晰。
LangChain提供了组件(回调机制),让我们可以捕获Agent的完整思考链。
from langchain.callbacks.base import BaseCallbackHandler
from langchain.schema import AgentAction, LLMResult
from typing import Any, Dict, List
class IterationCounterCallback(BaseCallbackHandler):
"""一个在每次Agent行动前打印轮次的回调处理器"""
def __init__(self):
self.iteration_count = 0
def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any:
self.iteration_count += 1
print(f"\n--- 🤔 思考轮次: {self.iteration_count} ---")
class AgentTraceCallback(BaseCallbackHandler):
"""一个捕获并存储Agent与LLM之间完整交互记录的回调处理器"""
def __init__(self):
self.trace = ""
def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> Any:
self.trace += f"\n--- PROMPT TO LLM ---\n{prompts[0]}\n"
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> Any:
self.trace += f"--- RESPONSE FROM LLM ---\n{response.generations[0][0].text}\n"
这些记录以配置的方式,在agent_executor.invoke调用中传入
response = agent_executor.invoke(
{"input": question},
# 回调列表可以包含多个回调处理器
config={"callbacks": [iteration_counter, trace_collector]}
)
- IterationCounterCallback
IterationCounterCallback 并不是 LangChain 的标准内置回调类,而更可能是一个自定义的回调处理器,用于在 LangChain 的执行过程中跟踪迭代次数或特定事件。这种回调通常用于记录某些操作的执行次数,例如在链(Chain)或代理(Agent)执行过程中统计某些特定步骤的调用次数。- 功能
迭代计数:IterationCounterCallback 通常用于记录某些特定操作(如 LLM 调用、工具调用、代理循环等)的次数。
监控和调试:通过计数,可以帮助开发者了解模型或代理在执行任务时的行为,例如循环次数、调用频率等。
自定义逻辑:可以根据计数结果触发特定行为,例如在达到一定次数后停止执行或记录日志。
还有AgentTraceCallback。
- 功能
这里不做多过记录了,具体详情见LangChain API。
3 Agent分析调优
我们需要从“效率”、“准确性”、“鲁棒性”三个角度去调整优化我们的Agent。
效率
Q:Agent的ReAct循环是否偏离预期?
Q:步骤是否多余(需要合并)?准确性
Q:Agent选择搜索的关键词是否有效?
Q:使用工具获取的内容是否真的回答了问题?
Q:最终的答案是否准确、全面?鲁棒性
Q:如果其中一个工具调用失败了(比如网页无法访问),Agent要怎么处理?
Q:故障怎么恢复?
3.1 使用LLM自评LLM-as-a-Judge
人工评估在复杂场景往往效率低下,更适合的做法是训练一个LLM评委。
实现"LLM即评委"。
我们将创建一个新的函数,它的职责就是扮演“评委”的角色。它会接收我们捕获到的agent_trace,然后调用一个强大的LLM(我们继续使用DeepSeek),根据我们设定的标准来对这次运行进行打分和评价。
- Prompt :“评委LLM”的评分标准
EVALUATION_PROMPT_TEMPLATE = """
**角色:** 你是一位经验丰富的AI Agent评审专家。
**任务:** 请根据以下提供的“原始问题”、“Agent最终答案”和“Agent完整思考轨迹”,对该Agent的表现进行一次全面、客观的评估。
**评估标准:**
1. **效率 (Efficiency):** Agent是否采取了最直接、最少的步骤来解决问题?是否存在冗余的工具调用或不必要的思考循环?
2. **准确性 (Accuracy):** Agent的最终答案是否准确、全面地回答了原始问题?
3. **忠实度 (Faithfulness):** 最终答案是否严格基于其“观察结果”(工具的输出)?是否存在编造或幻觉的成分?
4. **工具使用 (Tool Use):** Agent是否选择了正确的工具?传递给工具的参数是否合理?
---
**【待评估材料】**
**原始问题:**
{question}
**Agent最终答案:**
{final_answer}
**Agent完整思考轨迹:**
---
{agent_trace}
---
**【你的评估报告】**
请在下方提供你的评估报告。请先给出一个总体得分(满分10分),然后分点阐述你的理由,并提出具体的改进建议。
**总体得分:** [请在这里打分,例如:8/10]
**详细评估与改进建议:**
1. **效率:** ...
2. **准确性:** ...
3. **忠实度:** ...
4. **工具使用:** ...
"""
- “评委”代码
from langchain_deepseek import ChatDeepSeek
from langchain.prompts import PromptTemplate
from langchain.schema.output_parser import StrOutputParser
from ..llms.llm_clients import create_deepseek_llm # 从我们的llm模块导入
from ..configs.prompt_config import EVALUATION_PROMPT_TEMPLATE
def evaluate_agent_trace(question: str, agent_trace: str, final_answer: str):
"""
使用LLM作为评委,来评估Agent的执行轨迹和最终答案。
"""
print("\n--- 启动LLM评委进行评估 ---")
# 1. 创建评估链 (Evaluation Chain)
prompt = PromptTemplate.from_template(EVALUATION_PROMPT_TEMPLATE)
llm = create_deepseek_llm() # 复用我们创建LLM的函数
# 使用LCEL(LangChain Expression Language)来构建链
# 这是一个简单的 "Prompt -> LLM -> String Output" 链
evaluation_chain = prompt | llm | StrOutputParser()
# 2. 运行评估链
try:
print("评委正在审阅材料并生成报告...")
evaluation_report = evaluation_chain.invoke({
"question": question,
"agent_trace": agent_trace,
"final_answer": final_answer
})
print("\n--- 评委报告生成完毕 ---")
print(evaluation_report)
except Exception as e:
print(f"\n--- LLM评委在评估时发生错误 ---")
print(e)
AI给出来的评估内容如下:
--- 评委报告生成完毕 ---
**总体得分:** 8.5/10
**详细评估与改进建议:**
1. **效率:**
- **评分:8/10**
- **理由:** Agent通过三次`search_tool`调用逐步获取信息,逻辑清晰,但首次搜索查询("Java vs Python for building LLM applications...")的输入参数 过于宽泛,导致返回结果包含冗余信息(如Vert.x与Asyncio的无关对比)。后续两次搜索(Python/Java框架)更精准,效率较高。
- **改进建议:** 首次搜索可拆分为两个针对性查询(如分别搜索优劣势和框架),或直接使用更精确的关键词(如"LLM framework comparison Java Python 2024")。
2. **准确性:**
- **评分:9/10**
- **理由:** 最终答案全面覆盖了两种语言的优劣势,且列出的框架(如LangChain、Spring AI)与搜索结果一致。但未明确提及Python的GIL(全局解释器锁)对 多线程的限制,这是Python性能劣势的关键细节。
- **改进建议:** 补充Python的GIL问题,并对比Java的JVM优化对LLM推理的潜在优势。
3. **忠实度:**
- **评分:9/10**
- **理由:** 答案严格基于工具返回的观察结果,未发现编造内容。但未完全引用搜索中提到的"Konduit支持模型加载与调优"的具体功能描述,略显简略。
- **改进建议:** 引用工具返回的原文关键描述(如"Konduit允许加载GPT/BERT模型")以增强可信度。
4. **工具使用:**
- **评分:8/10**
- **理由:** 工具选择合理(仅需搜索无需爬取),但首次搜索参数可优化。未尝试组合查询(如"Java LLM frameworks performance benchmarks")以获取更深度的对比数据。
- **改进建议:** 在对比优劣势时,可增加一次搜索查询(如"Python Java LLM latency memory usage")补充量化指标。
**其他建议:**
- 可增加对跨语言生态的说明(如Java通过Jython调用Python库的可行性),以体现更全面的视角。
- 最终答案中可补充框架的典型应用场景(如"Haystack适合RAG"已提及,但Spring AI的企业集成场景可细化)。
4 TODO
当前阶段就了解到这里,附一张单Agent LLM应用流程图。
觉得是符合MVP最小可行性原则的。
实际应用一般是多智能体协作 (Multi-Agent Collaboration),从ReAct模板引导来说,创建多个职责单一、能力专精的Agent,然后让它们组成一个团队来协同工作显然更好。
像微服务架构,又是要设计通信协作、故障处理、统一管理等一揽子咯。
微服务,我都计划从Java到LLM应用开发了,老熟人了。