Spaces:
Running
Running
| import os | |
| import sqlite3 | |
| from typing import Annotated, TypedDict | |
| from dotenv import load_dotenv | |
| from langchain_openai import ChatOpenAI | |
| from langchain_core.messages import SystemMessage # Added for persona | |
| from langgraph.graph import StateGraph, END | |
| from langgraph.graph.message import add_messages | |
| from langgraph.prebuilt import ToolNode | |
| from langgraph.checkpoint.sqlite import SqliteSaver | |
| from tools import tools | |
| from runtime_config import CHECKPOINTS_DB_PATH | |
| load_dotenv() | |
| DEEPSEEK_MODEL = os.getenv("DEEPSEEK_MODEL", "deepseek-chat") | |
| DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com") | |
| # 1. Define State | |
| class AgentState(TypedDict): | |
| messages: Annotated[list, add_messages] | |
| # 2. Setup LLM & Tools | |
| llm = ChatOpenAI( | |
| model=DEEPSEEK_MODEL, | |
| openai_api_key=os.getenv("DEEPSEEK_API_KEY"), | |
| openai_api_base=DEEPSEEK_BASE_URL, | |
| temperature=0 | |
| ) | |
| llm_with_tools = llm.bind_tools(tools) | |
| # 3. Define Nodes | |
| def call_model(state: AgentState): | |
| """ | |
| Node for LLM reasoning with a professional persona and structured logic. | |
| """ | |
| # Define the core instructions for the AI | |
| # 在 call_model 函数中修改 SystemMessage | |
| system_message = SystemMessage(content="""You are the 'U2CHAT bot', a professional stock analysis assistant. | |
| Your goal is to provide objective and analytical financial guidance. | |
| Core Directives: | |
| 1. Language: Respond exclusively in English. | |
| 2. Brevity & Structure: Be concise. Use clear headings (###), bullet points, and short paragraphs. Avoid "walls of text". | |
| 3. Clean Formatting: Strictly DO NOT use double asterisks (**) for emphasis within sentences or around single words. Use standard Markdown headers for organization only. | |
| 4. Data Visualization: Whenever analyzing a stock (e.g., K-line trends, price history), you MUST provide the data in a structured JSON block wrapped in ```json-chart``` tags. | |
| Format: | |
| ```json-chart | |
| { | |
| "type": "line", | |
| "title": "Title of the Chart", | |
| "labels": ["Jan", "Feb", "Mar"], | |
| "data": [100, 120, 110] | |
| } | |
| ``` | |
| Supported types: "line", "bar". Ensure 'labels' and 'data' arrays have the same length. | |
| Response Template: | |
| ### Market Overview: (1-2 sentences on current status) | |
| ### Key Data: (Include the json-chart block here if applicable, or a Markdown Table) | |
| ### Technical Analysis: (Brief bullet points on trends/indicators) | |
| ### Visual Trend: (A simple ASCII-based visual or directional emoji chart) | |
| Tone: Professional, objective, and analytical.""") | |
| # Inject the system message at the start of the conversation | |
| messages = [system_message] + state["messages"] | |
| response = llm_with_tools.invoke(messages) | |
| return {"messages": [response]} | |
| def route_logic(state: AgentState): | |
| """Router to decide if tools are needed.""" | |
| last_msg = state["messages"][-1] | |
| return "tools_executor" if last_msg.tool_calls else END | |
| # 4. Persistence Setup | |
| # Connection remains open for the lifecycle of the app | |
| conn = sqlite3.connect(str(CHECKPOINTS_DB_PATH), check_same_thread=False) | |
| memory = SqliteSaver(conn) | |
| # 5. Build Graph | |
| workflow = StateGraph(AgentState) | |
| workflow.add_node("llm_reasoning", call_model) | |
| workflow.add_node("tools_executor", ToolNode(tools)) | |
| workflow.set_entry_point("llm_reasoning") | |
| workflow.add_conditional_edges("llm_reasoning", route_logic) | |
| workflow.add_edge("tools_executor", "llm_reasoning") | |
| # 6. Compile with Checkpointer | |
| stock_agent_app = workflow.compile(checkpointer=memory) | |