Files
X-Agents/agent/app/agent/llm/anthropic.py
DESKTOP-72TV0V4\caoxiaozhu 5c435ab21e Add streaming support and refactor Chat UI
- Add run_stream method to AgentCore for streaming output
- Add base_url parameter to LLM clients for OpenRouter support
- Add xbot module for new agent implementation
- Refactor Chat.vue into composable + components (ChatHeader, ChatMessage, ChatInput, ChatSidebar, ChatAgentSelector)
- Add ChatStream handler for SSE streaming in Go server
- Add UseXBot field to chat request

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 10:49:44 +08:00

137 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Anthropic LLM 实现
"""
import os
from typing import Dict, Any, List, Optional
from anthropic import AsyncAnthropic
class AnthropicLLM:
"""Anthropic Claude LLM"""
def __init__(self, model_name: str = "claude-3-sonnet-20240229", api_key: Optional[str] = None, base_url: Optional[str] = None):
self.model_name = model_name
# 支持自定义 base_url如 OpenRouter
if base_url:
self.client = AsyncAnthropic(
api_key=api_key or os.getenv("ANTHROPIC_API_KEY", ""),
base_url=base_url
)
else:
self.client = AsyncAnthropic(
api_key=api_key or os.getenv("ANTHROPIC_API_KEY", "")
)
async def decide(self, prompt: str) -> Dict[str, Any]:
"""
LLM 决策 - 判断是否需要调用技能
Args:
prompt: 完整的 Prompt
Returns:
Dict: 包含 needs_skill, tool_calls, response 等
"""
system_prompt = """你是一个智能助手。请分析用户请求,判断是否需要调用工具来回答问题。
如果需要调用工具,请按以下格式返回 JSON
{"needs_skill": true, "tool_calls": [{"skill_id": "工具名称", "parameters": {"参数": ""}, "reason": "调用原因"}]}
如果不需要调用工具,请返回:
{"needs_skill": false, "response": "直接回答用户的内容"}
请只返回 JSON不要其他内容。"""
try:
response = await self.client.messages.create(
model=self.model_name,
max_tokens=2000,
system=system_prompt,
messages=[{"role": "user", "content": prompt}]
)
content = response.content[0].text
# 尝试解析 JSON
import json
try:
result = json.loads(content)
return result
except json.JSONDecodeError:
return {
"needs_skill": False,
"response": content
}
except Exception as e:
return {
"needs_skill": False,
"response": f"LLM 调用失败: {str(e)}"
}
async def generate(self, prompt: str, tool_results: List[Dict]) -> str:
"""
生成回复
Args:
prompt: 完整的 Prompt
tool_results: 工具调用结果
Returns:
str: 生成的回复
"""
user_message = prompt
# 添加工具结果作为上下文
if tool_results:
tool_context = "\n\n工具返回结果:\n"
for result in tool_results:
if result.get("success"):
tool_context += f"- {result.get('skill_id')}: {result.get('result')}\n"
user_message += tool_context
try:
response = await self.client.messages.create(
model=self.model_name,
max_tokens=4000,
messages=[{"role": "user", "content": user_message}]
)
return response.content[0].text
except Exception as e:
return f"生成回复失败: {str(e)}"
async def generate_stream(self, prompt: str, tool_results: List[Dict]):
"""
流式生成回复
Args:
prompt: 完整的 Prompt
tool_results: 工具调用结果
Yields:
str: 生成的回复片段
"""
user_message = prompt
# 添加工具结果作为上下文
if tool_results:
tool_context = "\n\n工具返回结果:\n"
for result in tool_results:
if result.get("success"):
tool_context += f"- {result.get('skill_id')}: {result.get('result')}\n"
user_message += tool_context
try:
async with self.client.messages.stream(
model=self.model_name,
max_tokens=4000,
messages=[{"role": "user", "content": user_message}]
) as stream:
async for text in stream.text_stream:
yield text
except Exception as e:
yield f"生成回复失败: {str(e)}"