15.3.2026 change configuration
This commit is contained in:
parent
4197ba38ae
commit
7ded74b65e
@ -13,4 +13,6 @@ README.md
|
|||||||
docker-compose.yml
|
docker-compose.yml
|
||||||
Dockerfile
|
Dockerfile
|
||||||
.dockerignore
|
.dockerignore
|
||||||
|
.chainlit/translations/*
|
||||||
|
!.chainlit/translations/en-US.json
|
||||||
.files/
|
.files/
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -181,5 +181,10 @@ fabric.properties
|
|||||||
# Android studio 3.1+ serialized cache file
|
# Android studio 3.1+ serialized cache file
|
||||||
.idea/caches/build_file_checksums.ser
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
# Chainlit correction
|
||||||
|
.chainlit/translations/*
|
||||||
|
!.chainlit/translations/en-US.json
|
||||||
|
.files/
|
||||||
|
|
||||||
# idea folder, uncomment if you don't need it
|
# idea folder, uncomment if you don't need it
|
||||||
.idea
|
.idea
|
||||||
109
README.md
109
README.md
@ -1,109 +0,0 @@
|
|||||||
# Právny AI Asistent – integrácia s API
|
|
||||||
|
|
||||||
## POPIS
|
|
||||||
|
|
||||||
Právny AI Asistent je inteligentný asistent integrovaný s oficiálnymi verejnými API Ministerstva spravodlivosti
|
|
||||||
Slovenskej republiky. Systém poskytuje užívateľom prístup k štruktúrovaným právnym informáciám prostredníctvom
|
|
||||||
prirodzeného jazyka.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
**Hlavné funkcie:**
|
|
||||||
- Interpretácia užívateľských otázok v prirodzenom jazyku
|
|
||||||
- Automatická extrakcia a validácia parametrov
|
|
||||||
- Načítavanie dát výlučne cez oficiálne API
|
|
||||||
- Prezentácia výsledkov v zrozumiteľnom formáte v slovenskom jazyku
|
|
||||||
|
|
||||||
## POUŽITÉ KNIŽNICE A TECHNOLÓGIE
|
|
||||||
|
|
||||||
- **Python** – hlavný programovací jazyk
|
|
||||||
- **openai-agent** – tvorba AI agentov od OpenAI
|
|
||||||
- **httpx** – asynchrónna komunikácia s API
|
|
||||||
- **Chainlit** – framework pre chat rozhranie
|
|
||||||
- **Ollama** – lokálne používanie AI modelov
|
|
||||||
- **Docker** – kontajnerizácia aplikácie
|
|
||||||
- a **iné**
|
|
||||||
|
|
||||||
## ŠTRUKTÚRA PROJEKTU
|
|
||||||
```
|
|
||||||
ai-lawyer-agent/
|
|
||||||
├── api/ # API logika
|
|
||||||
│ └── tools/ # API nástroje
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ ├── fetch_api_data.py # Spracovanie API
|
|
||||||
│ └── schemas.py # Pydantic schémy
|
|
||||||
├── core/
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ ├── initialize_agent.py # Inicializácia AI agenta
|
|
||||||
│ ├── stream_response.py # Streamovanie odpovedí
|
|
||||||
│ └── system_prompt.py # Systémový prompt
|
|
||||||
├── public/ # Verejné zdroje
|
|
||||||
│ ├── icon/ # Ikony a logá
|
|
||||||
│ │ ├── favicon.png
|
|
||||||
│ │ ├── logo_dark.png
|
|
||||||
│ │ └── logo_light.png
|
|
||||||
│ ├── img/
|
|
||||||
│ ├── styles.css # Štýly rozhrania
|
|
||||||
│ └── theme.json # Nastavenia témy
|
|
||||||
├── .chainlit/ # Markdown pre Chainlit
|
|
||||||
└── app.py # Hlavný súbor aplikácie (Сhainlit + spracovanie API)
|
|
||||||
```
|
|
||||||
|
|
||||||
## INŠTRUKCIA PRE INŠTALÁCIU
|
|
||||||
|
|
||||||
### **Inštalácia cez Git**
|
|
||||||
|
|
||||||
### 1. **Naklonovanie repozitára**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone git@git.kemt.fei.tuke.sk:od059jr/ai-lawyer-agent.git
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. **Vytvorenie virtuálneho prostredia**
|
|
||||||
|
|
||||||
#### Pre Windows
|
|
||||||
```bash
|
|
||||||
python -m venv venv
|
|
||||||
|
|
||||||
venv\Scripts\activate
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Pre macOS/Linux
|
|
||||||
```
|
|
||||||
python3 -m venv venv
|
|
||||||
|
|
||||||
source venv/bin/activate
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. **Inštalácia závislostí**
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. **Sťahovanie inštalátora Ollama**
|
|
||||||
|
|
||||||
https://ollama.com/download
|
|
||||||
|
|
||||||
|
|
||||||
### 5. **Sťahovanie modelov**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ollama pull qwen3-next:80b-cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ollama pull gpt-oss:20b-cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ollama pull deepseek-v3.1:671b-cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
### **Inštalácia cez DOCKER**
|
|
||||||
|
|
||||||
### 1. Inštalácia Docker a Docker Desktop
|
|
||||||
|
|
||||||
### 2. Nainštalujte a spustite aplikáciu
|
|
||||||
|
|
||||||
https://hub.docker.com/r/alexgott0616/ai-lawyer-agent
|
|
||||||
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import httpx
|
import httpx
|
||||||
import logging
|
import logging
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from tenacity import retry, stop_after_attempt, wait_exponential
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
@ -20,11 +21,12 @@ def docstring_from_model(model: type[BaseModel]):
|
|||||||
return func
|
return func
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=5))
|
||||||
async def fetch_api_data(icon: str, url: str, params: dict, remove_keys: list = None) -> dict:
|
async def fetch_api_data(icon: str, url: str, params: dict, remove_keys: list = None) -> dict:
|
||||||
try:
|
try:
|
||||||
logger.info(f"\n🔨{icon}Input parameters: {params}\n")
|
logger.info(f"\n🔨{icon}Input parameters: {params}\n")
|
||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client:
|
||||||
response = await client.get(url, params=params, timeout=10.0)
|
response = await client.get(url, params=params)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
logger.debug(f"\n🖇️{icon}Request URL: {response.url}\n")
|
logger.debug(f"\n🖇️{icon}Request URL: {response.url}\n")
|
||||||
|
|
||||||
@ -33,13 +35,11 @@ async def fetch_api_data(icon: str, url: str, params: dict, remove_keys: list =
|
|||||||
for key in remove_keys:
|
for key in remove_keys:
|
||||||
data.pop(key, None)
|
data.pop(key, None)
|
||||||
logger.info(f"\n🚮{icon}Successfully removed key: {key}\n")
|
logger.info(f"\n🚮{icon}Successfully removed key: {key}\n")
|
||||||
|
|
||||||
logger.info(f"\n✅{icon}Successfully fetched data from {url}\n")
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
except httpx.HTTPStatusError as e:
|
except httpx.HTTPStatusError as e:
|
||||||
logger.error(f"\n{icon}HTTP error: {e.response.status_code} - {e.response.text}\n")
|
logger.error(f"\n{icon}HTTP error: {e.response.status_code} - {e.response.text}\n")
|
||||||
return {"error": "http_error", "status_code": e.response.status_code, "detail": e.response.text}
|
return {"error": "http_error", "status_code": e.response.status_code}
|
||||||
except httpx.RequestError as e:
|
except httpx.RequestError as e:
|
||||||
logger.error(f"\n{icon}Request error: {str(e)}\n")
|
logger.error(f"\n{icon}Request error: {str(e)}\n")
|
||||||
return {"error": "request_error", "status_code": str(e)}
|
return {"error": "request_error", "status_code": str(e)}
|
||||||
|
|||||||
74
app.py
74
app.py
@ -1,76 +1,54 @@
|
|||||||
import os
|
|
||||||
import chainlit as cl
|
import chainlit as cl
|
||||||
|
|
||||||
from core.initialize_agent import assistant_agent
|
from core.init_agent import assistant_agent
|
||||||
from core.stream_response import stream_response
|
from core.stream_response import stream_response
|
||||||
|
|
||||||
def clear_screen():
|
DEFAULT_MODEL = "qwen3.5:cloud"
|
||||||
os.system('cls' if os.name == 'nt' else 'clear')
|
|
||||||
|
STARTERS = [
|
||||||
|
("What legal data can the agent find?","magnifying_glass"),
|
||||||
|
("What is the agent not allowed to do or use?","ban"),
|
||||||
|
("What are the details of your AI model?","hexagon"),
|
||||||
|
("What data sources does the agent rely on?","database"),
|
||||||
|
]
|
||||||
|
|
||||||
|
PROFILES = [
|
||||||
|
("qwen3.5:cloud","Qwen 3.5 CLOUD"),
|
||||||
|
("gpt-oss:20b-cloud","GPT-OSS 20B CLOUD"),
|
||||||
|
]
|
||||||
|
|
||||||
@cl.set_starters
|
@cl.set_starters
|
||||||
async def set_starters():
|
async def set_starters():
|
||||||
return [
|
return [
|
||||||
cl.Starter(
|
cl.Starter(label=label, message=label, icon=f"/public/icon/{icon}.svg")
|
||||||
label="What legal data can the agent find?",
|
for label, icon in STARTERS
|
||||||
message="What legal data can the agent find?",
|
|
||||||
icon="/public/icon/magnifying_glass.svg",
|
|
||||||
),
|
|
||||||
cl.Starter(
|
|
||||||
label="What is the agent not allowed to do or use?",
|
|
||||||
message="What is the agent not allowed to do or use?",
|
|
||||||
icon="/public/icon/ban.svg",
|
|
||||||
),
|
|
||||||
cl.Starter(
|
|
||||||
label="What are the details of your AI model?",
|
|
||||||
message="What are the details of your AI model?",
|
|
||||||
icon="/public/icon/hexagon.svg",
|
|
||||||
),
|
|
||||||
cl.Starter(
|
|
||||||
label="What data sources does the agent rely on?",
|
|
||||||
message="What data sources does the agent rely on?",
|
|
||||||
icon="/public/icon/database.svg",
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@cl.set_chat_profiles
|
@cl.set_chat_profiles
|
||||||
async def chat_profile():
|
async def chat_profile():
|
||||||
return [
|
return [
|
||||||
cl.ChatProfile(
|
cl.ChatProfile(name=name, markdown_description=f"Uses **{desc}**")
|
||||||
name="qwen3-next:80b-cloud",
|
for name, desc in PROFILES
|
||||||
markdown_description="Uses **Qwen 3 Next 80B**",
|
|
||||||
),
|
|
||||||
cl.ChatProfile(
|
|
||||||
name="gpt-oss:20b-cloud",
|
|
||||||
markdown_description="Uses **GPT-OSS 20B CLOUD**",
|
|
||||||
),
|
|
||||||
cl.ChatProfile(
|
|
||||||
name="deepseek-v3.1:671b-cloud",
|
|
||||||
markdown_description="Uses **DEEPSEEK v3.1 671B CLOUD**",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@cl.on_chat_start
|
@cl.on_chat_start
|
||||||
async def start():
|
async def start():
|
||||||
clear_screen()
|
model_name = cl.user_session.get("chat_profile") or DEFAULT_MODEL
|
||||||
|
cl.user_session.set("agent", assistant_agent(model_name))
|
||||||
cl.user_session.set("message_history", [])
|
cl.user_session.set("message_history", [])
|
||||||
|
|
||||||
profile = cl.user_session.get("chat_profile")
|
|
||||||
model_name = profile or "qwen3-next:80b-cloud"
|
|
||||||
agent = assistant_agent(model_name)
|
|
||||||
cl.user_session.set("agent", agent)
|
|
||||||
|
|
||||||
|
|
||||||
@cl.on_message
|
@cl.on_message
|
||||||
async def main(message: cl.Message):
|
async def main(message: cl.Message):
|
||||||
agent = cl.user_session.get("agent")
|
agent = cl.user_session.get("agent")
|
||||||
message_history = cl.user_session.get("message_history")
|
history = cl.user_session.get("message_history")
|
||||||
|
|
||||||
message_history.append({"role": "user", "content": message.content})
|
history.append({"role": "user", "content": message.content})
|
||||||
|
|
||||||
msg = cl.Message(content="")
|
msg = cl.Message(content="")
|
||||||
async for token in stream_response(agent, message_history):
|
async for token in stream_response(agent, history):
|
||||||
await msg.stream_token(token)
|
await msg.stream_token(token)
|
||||||
|
|
||||||
message_history.append({"role": "assistant", "content": msg.content})
|
|
||||||
await msg.update()
|
await msg.update()
|
||||||
|
|
||||||
|
if msg.content:
|
||||||
|
history.append({"role": "assistant", "content": msg.content})
|
||||||
@ -1,10 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
from agents import Agent, AgentHooks
|
from agents import Agent, AgentHooks
|
||||||
from agents import OpenAIChatCompletionsModel, AsyncOpenAI, ModelSettings
|
from agents import OpenAIChatCompletionsModel, AsyncOpenAI, ModelSettings
|
||||||
from agents import set_tracing_disabled, enable_verbose_stdout_logging
|
from agents import set_tracing_disabled
|
||||||
|
|
||||||
from core.system_prompt import get_system_prompt
|
from core.system_prompt import get_system_prompt
|
||||||
|
|
||||||
from api.tools.judges import JudgesAPI
|
from api.tools.judges import JudgesAPI
|
||||||
from api.tools.coutrs import CourtsAPI
|
from api.tools.coutrs import CourtsAPI
|
||||||
from api.tools.contracts import ContractsAPI
|
from api.tools.contracts import ContractsAPI
|
||||||
@ -12,18 +11,16 @@ from api.tools.decisions import DecisionsAPI
|
|||||||
from api.tools.admin_proceedings import AdminProceedingsAPI
|
from api.tools.admin_proceedings import AdminProceedingsAPI
|
||||||
from api.tools.civil_proceedings import CivilProceedingsAPI
|
from api.tools.civil_proceedings import CivilProceedingsAPI
|
||||||
|
|
||||||
ollama_host = os.getenv("OLLAMA_HOST", "host.docker.internal:11434")
|
|
||||||
|
|
||||||
set_tracing_disabled(True)
|
set_tracing_disabled(True)
|
||||||
enable_verbose_stdout_logging()
|
|
||||||
|
|
||||||
class BaseAgent(Agent[None]): pass
|
|
||||||
|
|
||||||
class MyAgentHooks(AgentHooks):
|
class MyAgentHooks(AgentHooks):
|
||||||
async def on_start(self, context, agent): print(f"\n🏃♂️➡️[AgentHooks] {agent.name} started.")
|
async def on_start(self, context, agent):
|
||||||
async def on_end(self, context, agent, output): print(f"\n🏁[AgentHooks] {agent.name} ended.")
|
print(f"\n🏃♂️➡️ [AgentHooks] {agent.name} started.")
|
||||||
|
|
||||||
def assistant_agent(model_name: str = "qwen3-next:80b-cloud") -> BaseAgent:
|
async def on_end(self, context, agent, output):
|
||||||
|
print(f"🏁 [AgentHooks] {agent.name} ended.")
|
||||||
|
|
||||||
|
def assistant_agent(model_name: str = "qwen3.5:cloud") -> Agent:
|
||||||
|
|
||||||
judge_api = JudgesAPI()
|
judge_api = JudgesAPI()
|
||||||
court_api = CourtsAPI()
|
court_api = CourtsAPI()
|
||||||
@ -32,19 +29,14 @@ def assistant_agent(model_name: str = "qwen3-next:80b-cloud") -> BaseAgent:
|
|||||||
admin_proceedings_api = AdminProceedingsAPI()
|
admin_proceedings_api = AdminProceedingsAPI()
|
||||||
civil_proceedings_api = CivilProceedingsAPI()
|
civil_proceedings_api = CivilProceedingsAPI()
|
||||||
|
|
||||||
client = AsyncOpenAI(base_url=f"http://{ollama_host}/v1", api_key="ollama")
|
client = AsyncOpenAI(base_url=f"http://localhost:11434/v1", api_key="ollama", timeout=120.0, max_retries=0)
|
||||||
|
|
||||||
model = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
|
model = OpenAIChatCompletionsModel(model=model_name, openai_client=client)
|
||||||
|
|
||||||
agent = BaseAgent(
|
agent = Agent(
|
||||||
name="Assistant",
|
name="Assistant",
|
||||||
instructions=get_system_prompt(model_name),
|
instructions=get_system_prompt(model_name),
|
||||||
model=model,
|
model=model,
|
||||||
model_settings=ModelSettings(
|
model_settings=ModelSettings(temperature=0.7, tool_choice="auto", parallel_tool_calls=False),
|
||||||
temperature=0.7,
|
|
||||||
tool_choice="auto",
|
|
||||||
parallel_tool_calls=False,
|
|
||||||
),
|
|
||||||
tools=[
|
tools=[
|
||||||
judge_api.judge, judge_api.judge_id, judge_api.judge_autocomplete,
|
judge_api.judge, judge_api.judge_id, judge_api.judge_autocomplete,
|
||||||
court_api.court, court_api.court_id, court_api.court_autocomplete,
|
court_api.court, court_api.court_id, court_api.court_autocomplete,
|
||||||
@ -1,23 +1,13 @@
|
|||||||
from typing import Any, AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
from agents import Agent, Runner
|
from agents import Agent, Runner
|
||||||
from agents import RunHooks, ModelResponse
|
|
||||||
from openai.types.responses import ResponseTextDeltaEvent
|
from openai.types.responses import ResponseTextDeltaEvent
|
||||||
|
|
||||||
class MyRunHooks(RunHooks):
|
|
||||||
"""RunHooks for logging the lifecycle of an agent."""
|
|
||||||
async def on_agent_start(self, context, agent) -> None:
|
|
||||||
print(f"\n 🐾[RunHooks] {agent.name} started.\n")
|
|
||||||
|
|
||||||
async def on_agent_end(self, context, agent, output: Any) -> None:
|
|
||||||
print(f"\n 🚪[RunHooks] {agent.name} ended.\n")
|
|
||||||
|
|
||||||
async def stream_response(agent: Agent, prompt: str) -> AsyncGenerator[str, None]:
|
async def stream_response(agent: Agent, prompt: str) -> AsyncGenerator[str, None]:
|
||||||
"""Stream agent response and update the UI."""
|
"""Stream agent response and update the UI."""
|
||||||
try:
|
try:
|
||||||
result = Runner.run_streamed(agent, input=prompt, hooks=MyRunHooks())
|
result = Runner.run_streamed(agent, input=prompt)
|
||||||
async for event in result.stream_events():
|
async for event in result.stream_events():
|
||||||
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
|
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
|
||||||
yield event.data.delta # <-- sends the next piece of response text
|
yield event.data.delta # <-- sends the next piece of response text
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
yield f"⚠️🖨️ Error: {e}"
|
yield f"⚠️🖨️ Error: {e}"
|
||||||
|
|||||||
@ -10,4 +10,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- PYTHONPATH=/main
|
- PYTHONPATH=/main
|
||||||
- PYTHONUNBUFFERED=1
|
- PYTHONUNBUFFERED=1
|
||||||
|
- OLLAMA_HOST=ollama:11434
|
||||||
|
depends_on:
|
||||||
|
- ollama
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 97 KiB |
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user