Enhance interview functionality in Report Agent and Zep Tools

- Updated the "interview_agents" tool in the Report Agent to utilize the OASIS interview API for real-time agent interviews across Twitter and Reddit, providing authentic responses.
- Improved the ZepToolsService to streamline agent selection, question generation, and interview processing, ensuring a more efficient and structured interview workflow.
- Enhanced documentation to reflect the new interview capabilities, including updated usage instructions and important operational notes regarding the OASIS environment.
- Removed deprecated methods related to simulated interviews, focusing on real API interactions for improved accuracy and reliability.
This commit is contained in:
666ghj 2025-12-09 16:58:59 +08:00
parent b022c38d78
commit 74aab44766
2 changed files with 145 additions and 116 deletions

View file

@ -248,28 +248,31 @@ class ReportAgent:
}, },
"interview_agents": { "interview_agents": {
"name": "interview_agents", "name": "interview_agents",
"description": """【深度采访 - 多视角观点采集】 "description": """【深度采访 - 真实Agent采访双平台
采访模拟中的Agent角色获取来自不同视角的深度观点这是获取模拟结果中各方声音的最佳方式 调用OASIS模拟环境的采访API对正在运行的模拟Agent进行真实采访
这不是LLM模拟而是调用真实的采访接口获取模拟Agent的原始回答
默认在Twitter和Reddit两个平台同时采访获取更全面的观点
功能流程 功能流程
1. 自动读取人设文件了解所有模拟Agent 1. 自动读取人设文件了解所有模拟Agent
2. 智能选择与采访主题最相关的Agent如学生媒体官方等 2. 智能选择与采访主题最相关的Agent如学生媒体官方等
3. 模拟采访每个选中的Agent获取符合其人设的回答 3. 自动生成采访问题
4. 整合所有采访结果提供多视角分析 4. 调用 /api/simulation/interview/batch 接口在双平台进行真实采访
5. 整合所有采访结果提供多视角分析
使用场景 使用场景
- 需要从不同角色视角了解事件看法学生怎么看媒体怎么看官方怎么说 - 需要从不同角色视角了解事件看法学生怎么看媒体怎么看官方怎么说
- 需要收集多方意见和立场 - 需要收集多方意见和立场
- 需要获取模拟Agent的直接引言和观点 - 需要获取模拟Agent的真实回答来自OASIS模拟环境
- 想让报告更生动包含"采访实录" - 想让报告更生动包含"采访实录"
返回内容 返回内容
- 被采访Agent的身份信息 - 被采访Agent的身份信息
- 各Agent的采访回答符合其人设的原创内容 - 各Agent在Twitter和Reddit两个平台的采访回答
- 关键引言可直接引用 - 关键引言可直接引用
- 采访摘要和观点对比 - 采访摘要和观点对比
重要这是获取模拟Agent"真实声音"的唯一方式""", 重要需要OASIS模拟环境正在运行才能使用此功能""",
"parameters": { "parameters": {
"interview_topic": "采访主题或需求描述(如:'了解学生对宿舍甲醛事件的看法'", "interview_topic": "采访主题或需求描述(如:'了解学生对宿舍甲醛事件的看法'",
"max_agents": "最多采访的Agent数量可选默认5" "max_agents": "最多采访的Agent数量可选默认5"
@ -334,7 +337,7 @@ class ReportAgent:
return result.to_text() return result.to_text()
elif tool_name == "interview_agents": elif tool_name == "interview_agents":
# 深度采访 - 采访模拟Agent获取多视角观点 # 深度采访 - 调用真实的OASIS采访API获取模拟Agent的回答双平台
interview_topic = parameters.get("interview_topic", parameters.get("query", "")) interview_topic = parameters.get("interview_topic", parameters.get("query", ""))
max_agents = parameters.get("max_agents", 5) max_agents = parameters.get("max_agents", 5)
if isinstance(max_agents, str): if isinstance(max_agents, str):

View file

@ -1256,19 +1256,22 @@ class ZepToolsService:
""" """
InterviewAgents - 深度采访 InterviewAgents - 深度采访
采访模拟中的Agent获取多视角的深度观点 调用真实的OASIS采访API采访模拟中正在运行的Agent
1. 自动读取人设文件了解所有模拟Agent 1. 自动读取人设文件了解所有模拟Agent
2. 使用LLM分析采访需求智能选择最相关的Agent 2. 使用LLM分析采访需求智能选择最相关的Agent
3. 模拟采访每个选中的Agent获取符合其人设的回答 3. 使用LLM生成采访问题
4. 整合所有采访结果生成采访报告 4. 调用 /api/simulation/interview/batch 接口进行真实采访双平台同时采访
5. 整合所有采访结果生成采访报告
重要此功能需要模拟环境处于运行状态OASIS环境未关闭
使用场景 使用场景
- 需要从不同角色视角了解事件看法 - 需要从不同角色视角了解事件看法
- 需要收集多方意见和观点 - 需要收集多方意见和观点
- 需要模拟真实采访获取第一手资料 - 需要获取模拟Agent的真实回答非LLM模拟
Args: Args:
simulation_id: 模拟ID用于定位人设文件 simulation_id: 模拟ID用于定位人设文件和调用采访API
interview_requirement: 采访需求描述非结构化"了解学生对事件的看法" interview_requirement: 采访需求描述非结构化"了解学生对事件的看法"
simulation_requirement: 模拟需求背景可选 simulation_requirement: 模拟需求背景可选
max_agents: 最多采访的Agent数量 max_agents: 最多采访的Agent数量
@ -1277,8 +1280,9 @@ class ZepToolsService:
Returns: Returns:
InterviewResult: 采访结果 InterviewResult: 采访结果
""" """
import os from .simulation_runner import SimulationRunner
logger.info(f"InterviewAgents 深度采访: {interview_requirement[:50]}...")
logger.info(f"InterviewAgents 深度采访真实API: {interview_requirement[:50]}...")
result = InterviewResult( result = InterviewResult(
interview_topic=interview_requirement, interview_topic=interview_requirement,
@ -1296,8 +1300,8 @@ class ZepToolsService:
result.total_agents = len(profiles) result.total_agents = len(profiles)
logger.info(f"加载到 {len(profiles)} 个Agent人设") logger.info(f"加载到 {len(profiles)} 个Agent人设")
# Step 2: 使用LLM选择要采访的Agent # Step 2: 使用LLM选择要采访的Agent返回agent_id列表
selected_agents, selection_reasoning = self._select_agents_for_interview( selected_agents, selected_indices, selection_reasoning = self._select_agents_for_interview(
profiles=profiles, profiles=profiles,
interview_requirement=interview_requirement, interview_requirement=interview_requirement,
simulation_requirement=simulation_requirement, simulation_requirement=simulation_requirement,
@ -1306,7 +1310,7 @@ class ZepToolsService:
result.selected_agents = selected_agents result.selected_agents = selected_agents
result.selection_reasoning = selection_reasoning result.selection_reasoning = selection_reasoning
logger.info(f"选择了 {len(selected_agents)} 个Agent进行采访") logger.info(f"选择了 {len(selected_agents)} 个Agent进行采访: {selected_indices}")
# Step 3: 生成采访问题(如果没有提供) # Step 3: 生成采访问题(如果没有提供)
if not result.interview_questions: if not result.interview_questions:
@ -1317,25 +1321,113 @@ class ZepToolsService:
) )
logger.info(f"生成了 {len(result.interview_questions)} 个采访问题") logger.info(f"生成了 {len(result.interview_questions)} 个采访问题")
# Step 4: 对每个选中的Agent进行采访 # 将问题合并为一个采访prompt
for agent in selected_agents: combined_prompt = "\n".join([f"{i+1}. {q}" for i, q in enumerate(result.interview_questions)])
interview = self._conduct_interview(
agent=agent, # 添加优化前缀避免Agent调用工具而直接回复文本
questions=result.interview_questions, INTERVIEW_PROMPT_PREFIX = "结合你的人设、所有的过往记忆与行动,不调用任何工具直接用文本回复我:"
interview_requirement=interview_requirement, optimized_prompt = f"{INTERVIEW_PROMPT_PREFIX}{combined_prompt}"
simulation_requirement=simulation_requirement
# Step 4: 调用真实的采访API不指定platform默认双平台同时采访
try:
# 构建批量采访列表不指定platform双平台采访
interviews_request = []
for agent_idx in selected_indices:
interviews_request.append({
"agent_id": agent_idx,
"prompt": optimized_prompt # 使用优化后的prompt
# 不指定platformAPI会在twitter和reddit两个平台都采访
})
logger.info(f"调用批量采访API双平台: {len(interviews_request)} 个Agent")
# 调用 SimulationRunner 的批量采访方法不传platform双平台采访
api_result = SimulationRunner.interview_agents_batch(
simulation_id=simulation_id,
interviews=interviews_request,
platform=None, # 不指定platform双平台采访
timeout=180.0 # 双平台需要更长超时
) )
result.interviews.append(interview)
result.interviewed_count = len(result.interviews) logger.info(f"采访API返回: {api_result.get('interviews_count', 0)} 个结果, success={api_result.get('success')}")
# Step 5: 生成采访摘要 # 检查API调用是否成功
result.summary = self._generate_interview_summary( if not api_result.get("success", False):
interviews=result.interviews, error_msg = api_result.get("error", "未知错误")
interview_requirement=interview_requirement logger.warning(f"采访API返回失败: {error_msg}")
) result.summary = f"采访API调用失败{error_msg}。请检查OASIS模拟环境状态。"
return result
logger.info(f"InterviewAgents完成: 采访了 {result.interviewed_count} 个Agent") # Step 5: 解析API返回结果构建AgentInterview对象
# 双平台模式返回格式: {"twitter_0": {...}, "reddit_0": {...}, "twitter_1": {...}, ...}
api_data = api_result.get("result", {})
results_dict = api_data.get("results", {}) if isinstance(api_data, dict) else {}
for i, agent_idx in enumerate(selected_indices):
agent = selected_agents[i]
agent_name = agent.get("realname", agent.get("username", f"Agent_{agent_idx}"))
agent_role = agent.get("profession", "未知")
agent_bio = agent.get("bio", "")
# 获取该Agent在两个平台的采访结果
twitter_result = results_dict.get(f"twitter_{agent_idx}", {})
reddit_result = results_dict.get(f"reddit_{agent_idx}", {})
twitter_response = twitter_result.get("response", "")
reddit_response = reddit_result.get("response", "")
# 合并两个平台的回答
response_parts = []
if twitter_response:
response_parts.append(f"【Twitter平台回答】\n{twitter_response}")
if reddit_response:
response_parts.append(f"【Reddit平台回答】\n{reddit_response}")
if response_parts:
response_text = "\n\n".join(response_parts)
else:
response_text = "[无回复]"
# 提取关键引言(从两个平台的回答中)
import re
combined_responses = f"{twitter_response} {reddit_response}"
key_quotes = re.findall(r'[""「」『』]([^""「」『』]{10,100})[""「」『』]', combined_responses)
if not key_quotes:
sentences = combined_responses.split('')
key_quotes = [s.strip() + '' for s in sentences if len(s.strip()) > 20][:3]
interview = AgentInterview(
agent_name=agent_name,
agent_role=agent_role,
agent_bio=agent_bio[:150],
question=combined_prompt,
response=response_text,
key_quotes=key_quotes[:5]
)
result.interviews.append(interview)
result.interviewed_count = len(result.interviews)
except ValueError as e:
# 模拟环境未运行
logger.warning(f"采访API调用失败环境未运行: {e}")
result.summary = f"采访失败:{str(e)}。模拟环境可能已关闭请确保OASIS环境正在运行。"
return result
except Exception as e:
logger.error(f"采访API调用异常: {e}")
import traceback
logger.error(traceback.format_exc())
result.summary = f"采访过程发生错误:{str(e)}"
return result
# Step 6: 生成采访摘要
if result.interviews:
result.summary = self._generate_interview_summary(
interviews=result.interviews,
interview_requirement=interview_requirement
)
logger.info(f"InterviewAgents完成: 采访了 {result.interviewed_count} 个Agent双平台")
return result return result
def _load_agent_profiles(self, simulation_id: str) -> List[Dict[str, Any]]: def _load_agent_profiles(self, simulation_id: str) -> List[Dict[str, Any]]:
@ -1391,7 +1483,15 @@ class ZepToolsService:
simulation_requirement: str, simulation_requirement: str,
max_agents: int max_agents: int
) -> tuple: ) -> tuple:
"""使用LLM选择要采访的Agent""" """
使用LLM选择要采访的Agent
Returns:
tuple: (selected_agents, selected_indices, reasoning)
- selected_agents: 选中Agent的完整信息列表
- selected_indices: 选中Agent的索引列表用于API调用
- reasoning: 选择理由
"""
# 构建Agent摘要列表 # 构建Agent摘要列表
agent_summaries = [] agent_summaries = []
@ -1444,17 +1544,20 @@ class ZepToolsService:
# 获取选中的Agent完整信息 # 获取选中的Agent完整信息
selected_agents = [] selected_agents = []
valid_indices = []
for idx in selected_indices: for idx in selected_indices:
if 0 <= idx < len(profiles): if 0 <= idx < len(profiles):
selected_agents.append(profiles[idx]) selected_agents.append(profiles[idx])
valid_indices.append(idx)
return selected_agents, reasoning return selected_agents, valid_indices, reasoning
except Exception as e: except Exception as e:
logger.warning(f"LLM选择Agent失败使用默认选择: {e}") logger.warning(f"LLM选择Agent失败使用默认选择: {e}")
# 降级:随机选择前N个 # 降级:选择前N个
selected = profiles[:max_agents] selected = profiles[:max_agents]
return selected, "使用默认选择策略" indices = list(range(min(max_agents, len(profiles))))
return selected, indices, "使用默认选择策略"
def _generate_interview_questions( def _generate_interview_questions(
self, self,
@ -1503,83 +1606,6 @@ class ZepToolsService:
"您认为应该如何解决或改进这个问题?" "您认为应该如何解决或改进这个问题?"
] ]
def _conduct_interview(
self,
agent: Dict[str, Any],
questions: List[str],
interview_requirement: str,
simulation_requirement: str
) -> AgentInterview:
"""模拟采访单个Agent"""
agent_name = agent.get("realname", agent.get("username", "未知"))
agent_role = agent.get("profession", "未知")
agent_bio = agent.get("bio", "")
agent_persona = agent.get("persona", agent_bio)
# 将多个问题合并为一次采访
questions_text = "\n".join([f"{i+1}. {q}" for i, q in enumerate(questions)])
system_prompt = f"""你现在扮演以下角色进行采访:
角色名称{agent_name}
角色身份{agent_role}
角色简介{agent_bio}
详细人设
{agent_persona[:2000]}
重要
1. 你必须完全代入这个角色用第一人称回答
2. 你的回答必须符合角色的身份立场性格和说话风格
3. 引用角色人设中的具体观点和经历
4. 语言风格要符合角色特征学生更随性官方更正式
5. 表达真实的情感和态度"""
user_prompt = f"""采访背景:{simulation_requirement if simulation_requirement else interview_requirement}
记者提问
{questions_text}
请以{agent_name}的身份回答以上问题回答要体现角色的独特视角和立场"""
try:
response = self.llm.chat(
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=0.7,
max_tokens=2000
)
# 提取关键引言(包含引号的句子)
import re
key_quotes = re.findall(r'[""「」『』]([^""「」『』]{10,100})[""「」『』]', response)
if not key_quotes:
# 提取有力的陈述句
sentences = response.split('')
key_quotes = [s.strip() + '' for s in sentences if len(s.strip()) > 20][:3]
return AgentInterview(
agent_name=agent_name,
agent_role=agent_role,
agent_bio=agent_bio[:150],
question=questions_text,
response=response,
key_quotes=key_quotes[:5]
)
except Exception as e:
logger.error(f"采访 {agent_name} 失败: {e}")
return AgentInterview(
agent_name=agent_name,
agent_role=agent_role,
agent_bio=agent_bio[:150],
question=questions_text,
response=f"[采访失败: {str(e)}]",
key_quotes=[]
)
def _generate_interview_summary( def _generate_interview_summary(
self, self,
interviews: List[AgentInterview], interviews: List[AgentInterview],
@ -1602,7 +1628,7 @@ class ZepToolsService:
2. 指出观点的共识和分歧 2. 指出观点的共识和分歧
3. 突出有价值的引言 3. 突出有价值的引言
4. 客观中立不偏袒任何一方 4. 客观中立不偏袒任何一方
5. 控制在300-500""" 5. 控制在1000字内"""
user_prompt = f"""采访主题:{interview_requirement} user_prompt = f"""采访主题:{interview_requirement}