diff --git a/geaflow-ai/pom.xml b/geaflow-ai/pom.xml index 75cea1e79..006215b74 100644 --- a/geaflow-ai/pom.xml +++ b/geaflow-ai/pom.xml @@ -31,13 +31,14 @@ geaflow-ai - 3.5.1 + 3.5.1 5.10.1 1.7.15 1.2.17 2.17.1 3.4.4 8.11.2 + 2.2.4 @@ -96,6 +97,25 @@ ${lmax.disrupter.veresion} + + org.noear + solon-web + ${solon.version} + + + + com.google.code.gson + gson + ${gson.version} + + + + org.noear + solon-test + ${solon.version} + test + + org.apache.geaflow geaflow-api @@ -124,4 +144,30 @@ + + + + maven-assembly-plugin + + + + org.apache.geaflow.ai.client.GeaFlowMemoryClientCLI + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/GeaFlowMemoryServer.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/GeaFlowMemoryServer.java new file mode 100644 index 000000000..d39123183 --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/GeaFlowMemoryServer.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.geaflow.ai.common.util.SeDeUtil; +import org.apache.geaflow.ai.graph.*; +import org.apache.geaflow.ai.graph.io.*; +import org.apache.geaflow.ai.index.EntityAttributeIndexStore; +import org.apache.geaflow.ai.index.vector.KeywordVector; +import org.apache.geaflow.ai.search.VectorSearch; +import org.apache.geaflow.ai.service.ServerMemoryCache; +import org.apache.geaflow.ai.verbalization.Context; +import org.apache.geaflow.ai.verbalization.SubgraphSemanticPromptFunction; +import org.noear.solon.Solon; +import org.noear.solon.annotation.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Controller +public class GeaFlowMemoryServer { + + private static final Logger LOGGER = LoggerFactory.getLogger(GeaFlowMemoryServer.class); + + private static final String SERVER_NAME = "geaflow-memory-server"; + private static final int DEFAULT_PORT = 8080; + + private static final ServerMemoryCache CACHE = new ServerMemoryCache(); + + public static void main(String[] args) { + System.setProperty("solon.app.name", SERVER_NAME); + Solon.start(GeaFlowMemoryServer.class, args, app -> { + app.cfg().loadAdd("application.yml"); + int port = app.cfg().getInt("server.port", DEFAULT_PORT); + LOGGER.info("Starting {} on port {}", SERVER_NAME, port); + app.get("/", ctx -> { + ctx.output("GeaFlow AI Server is running..."); + }); + app.get("/health", ctx -> { + ctx.output("{\"status\":\"UP\",\"service\":\"" + SERVER_NAME + "\"}"); + }); + }); + } + + @Get + @Mapping("/api/test") + public String test() { + return "GeaFlow Memory Server is working!"; + } + + @Post + @Mapping("/graph/create") + public String createGraph(@Body String input) { + GraphSchema graphSchema = SeDeUtil.deserializeGraphSchema(input); + String graphName = graphSchema.getName(); + if (graphName == null || CACHE.getGraphByName(graphName) != null) { + throw new RuntimeException("Cannot create graph name: " + graphName); + } + Map entities = new HashMap<>(); + for (VertexSchema vertexSchema : graphSchema.getVertexSchemaList()) { + entities.put(vertexSchema.getName(), new VertexGroup(vertexSchema, new ArrayList<>())); + } + for (EdgeSchema edgeSchema : graphSchema.getEdgeSchemaList()) { + entities.put(edgeSchema.getName(), new EdgeGroup(edgeSchema, new ArrayList<>())); + } + MemoryGraph graph = new MemoryGraph(graphSchema, entities); + CACHE.putGraph(graph); + LocalMemoryGraphAccessor graphAccessor = new LocalMemoryGraphAccessor(graph); + LOGGER.info("Success to init empty graph."); + + EntityAttributeIndexStore indexStore = new EntityAttributeIndexStore(); + indexStore.initStore(new SubgraphSemanticPromptFunction(graphAccessor)); + LOGGER.info("Success to init EntityAttributeIndexStore."); + + GraphMemoryServer server = new GraphMemoryServer(); + server.addGraphAccessor(graphAccessor); + server.addIndexStore(indexStore); + LOGGER.info("Success to init GraphMemoryServer."); + CACHE.putServer(server); + + LOGGER.info("Success to init graph. SCHEMA: {}", graphSchema); + return "createGraph has been called, graphName: " + graphName; + } + + @Post + @Mapping("/graph/addEntitySchema") + public String addSchema(@Param("graphName") String graphName, + @Body String input) { + Graph graph = CACHE.getGraphByName(graphName); + if (graph == null) { + throw new RuntimeException("Graph not exist."); + } + if (!(graph instanceof MemoryGraph)) { + throw new RuntimeException("Graph cannot modify."); + } + MemoryMutableGraph memoryMutableGraph = new MemoryMutableGraph((MemoryGraph) graph); + Schema schema = SeDeUtil.deserializeEntitySchema(input); + String schemaName = schema.getName(); + if (schema instanceof VertexSchema) { + memoryMutableGraph.addVertexSchema((VertexSchema) schema); + } else if (schema instanceof EdgeSchema) { + memoryMutableGraph.addEdgeSchema((EdgeSchema) schema); + } else { + throw new RuntimeException("Cannt add schema: " + input); + } + return "addSchema has been called, schemaName: " + schemaName; + } + + @Post + @Mapping("/graph/getGraphSchema") + public String getSchema(@Param("graphName") String graphName) { + Graph graph = CACHE.getGraphByName(graphName); + if (graph == null) { + throw new RuntimeException("Graph not exist."); + } + if (!(graph instanceof MemoryGraph)) { + throw new RuntimeException("Graph cannot modify."); + } + return SeDeUtil.serializeGraphSchema(graph.getGraphSchema()); + } + + @Post + @Mapping("/graph/insertEntity") + public String addEntity(@Param("graphName") String graphName, + @Body String input) { + Graph graph = CACHE.getGraphByName(graphName); + if (graph == null) { + throw new RuntimeException("Graph not exist."); + } + if (!(graph instanceof MemoryGraph)) { + throw new RuntimeException("Graph cannot modify."); + } + MemoryMutableGraph memoryMutableGraph = new MemoryMutableGraph((MemoryGraph) graph); + List graphEntities = SeDeUtil.deserializeEntities(input); + + for (GraphEntity entity : graphEntities) { + if (entity instanceof GraphVertex) { + memoryMutableGraph.addVertex(((GraphVertex) entity).getVertex()); + } else { + memoryMutableGraph.addEdge(((GraphEdge) entity).getEdge()); + } + } + CACHE.getConsolidateServer().executeConsolidateTask( + CACHE.getServerByName(graphName).getGraphAccessors().get(0), memoryMutableGraph); + return "Success to add entities, num: " + graphEntities.size(); + } + + @Post + @Mapping("/graph/delEntity") + public String deleteEntity(@Param("graphName") String graphName, + @Body String input) { + Graph graph = CACHE.getGraphByName(graphName); + if (graph == null) { + throw new RuntimeException("Graph not exist."); + } + if (!(graph instanceof MemoryGraph)) { + throw new RuntimeException("Graph cannot modify."); + } + MemoryMutableGraph memoryMutableGraph = new MemoryMutableGraph((MemoryGraph) graph); + List graphEntities = SeDeUtil.deserializeEntities(input); + for (GraphEntity entity : graphEntities) { + if (entity instanceof GraphVertex) { + memoryMutableGraph.removeVertex(entity.getLabel(), + ((GraphVertex) entity).getVertex().getId()); + } else { + memoryMutableGraph.removeEdge(((GraphEdge) entity).getEdge()); + } + } + return "Success to remove entities, num: " + graphEntities.size(); + } + + @Post + @Mapping("/query/context") + public String createContext(@Param("graphName") String graphName) { + GraphMemoryServer server = CACHE.getServerByName(graphName); + if (server == null) { + throw new RuntimeException("Server not exist."); + } + String sessionId = server.createSession(); + CACHE.putSession(server, sessionId); + return sessionId; + } + + @Post + @Mapping("/query/exec") + public String execQuery(@Param("sessionId") String sessionId, + @Body String query) { + String graphName = CACHE.getGraphNameBySession(sessionId); + if (graphName == null) { + throw new RuntimeException("Graph not exist."); + } + GraphMemoryServer server = CACHE.getServerByName(graphName); + VectorSearch search = new VectorSearch(null, sessionId); + search.addVector(new KeywordVector(query)); + server.search(search); + Context context = server.verbalize(sessionId, + new SubgraphSemanticPromptFunction(server.getGraphAccessors().get(0))); + return context.toString(); + } + + @Post + @Mapping("/query/result") + public String getResult(@Param("sessionId") String sessionId) { + String graphName = CACHE.getGraphNameBySession(sessionId); + if (graphName == null) { + throw new RuntimeException("Graph not exist."); + } + GraphMemoryServer server = CACHE.getServerByName(graphName); + List result = server.getSessionEntities(sessionId); + return result.toString(); + } +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/GraphMemoryServer.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/GraphMemoryServer.java index f48324c7f..b571fe540 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/GraphMemoryServer.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/GraphMemoryServer.java @@ -20,9 +20,12 @@ package org.apache.geaflow.ai; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import org.apache.geaflow.ai.graph.GraphAccessor; +import org.apache.geaflow.ai.graph.GraphEntity; import org.apache.geaflow.ai.index.EmbeddingIndexStore; import org.apache.geaflow.ai.index.EntityAttributeIndexStore; import org.apache.geaflow.ai.index.IndexStore; @@ -37,7 +40,7 @@ public class GraphMemoryServer { - private final SessionManagement sessionManagement = SessionManagement.INSTANCE; + private final SessionManagement sessionManagement = new SessionManagement(); private final List graphAccessors = new ArrayList<>(); private final List indexStores = new ArrayList<>(); @@ -47,12 +50,20 @@ public void addGraphAccessor(GraphAccessor graph) { } } + public List getGraphAccessors() { + return graphAccessors; + } + public void addIndexStore(IndexStore indexStore) { if (indexStore != null) { indexStores.add(indexStore); } } + public List getIndexStores() { + return indexStores; + } + public String createSession() { String sessionId = sessionManagement.createSession(); if (sessionId == null) { @@ -84,7 +95,7 @@ public String search(VectorSearch search) { } private void applySearch(String sessionId, SearchOperator operator, VectorSearch search) { - SessionManagement manager = SessionManagement.INSTANCE; + SessionManagement manager = sessionManagement; if (!manager.sessionExists(sessionId)) { return; } @@ -107,4 +118,13 @@ public Context verbalize(String sessionId, VerbalizationFunction verbalizationFu return new Context(stringBuilder.toString()); } + public List getSessionEntities(String sessionId) { + List subGraphList = sessionManagement.getSubGraph(sessionId); + Set entitySet = new HashSet<>(); + for (SubGraph subGraph : subGraphList) { + entitySet.addAll(subGraph.getGraphEntityList()); + } + return new ArrayList<>(entitySet); + } + } diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/client/GeaFlowMemoryClientCLI.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/client/GeaFlowMemoryClientCLI.java new file mode 100644 index 000000000..870b0eb6b --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/client/GeaFlowMemoryClientCLI.java @@ -0,0 +1,369 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai.client; + +import com.google.gson.Gson; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.apache.geaflow.ai.common.config.Constants; +import org.apache.geaflow.ai.graph.io.*; + +public class GeaFlowMemoryClientCLI { + + private static final String BASE_URL = "http://localhost:8080"; + private static final String SERVER_URL = BASE_URL + "/api/test"; + private static final String CREATE_URL = BASE_URL + "/graph/create"; + private static final String SCHEMA_URL = BASE_URL + "/graph/addEntitySchema"; + private static final String INSERT_URL = BASE_URL + "/graph/insertEntity"; + private static final String CONTEXT_URL = BASE_URL + "/query/context"; + private static final String EXEC_URL = BASE_URL + "/query/exec"; + + private static final String DEFAULT_GRAPH_NAME = "memory_graph"; + private static final String VERTEX_LABEL = "chunk"; + private static final String EDGE_LABEL = "relation"; + private final Scanner scanner = new Scanner(System.in); + private final Gson gson = new Gson(); + private String currentGraphName = DEFAULT_GRAPH_NAME; + private String currentSessionId = null; + + public static void main(String[] args) { + GeaFlowMemoryClientCLI client = new GeaFlowMemoryClientCLI(); + client.start(); + } + + public void start() { + printWelcome(); + + while (true) { + try { + System.out.print("\ngeaflow> "); + String input = scanner.nextLine().trim(); + + if (input.isEmpty()) { + continue; + } + + if (input.equalsIgnoreCase("exit") || input.equalsIgnoreCase("quit")) { + System.out.println("Goodbye!"); + break; + } + + if (input.equalsIgnoreCase("help")) { + printHelp(); + continue; + } + + processCommand(input); + + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + if (e.getCause() != null) { + System.err.println("Cause: " + e.getCause().getMessage()); + } + } + } + + scanner.close(); + } + + private void processCommand(String command) throws IOException { + String[] parts = command.split("\\s+", 2); + String cmd = parts[0].toLowerCase(); + String param = parts.length > 1 ? parts[1] : ""; + + switch (cmd) { + case "test": + testServer(); + break; + + case "use": + currentGraphName = param.isEmpty() ? DEFAULT_GRAPH_NAME : param; + break; + + case "create": + String graphName = param.isEmpty() ? DEFAULT_GRAPH_NAME : param; + createGraph(graphName); + currentGraphName = graphName; + break; + + case "remember": + if (param.isEmpty()) { + System.out.println("Please enter content to remember:"); + param = scanner.nextLine(); + } + rememberContent(param); + break; + + case "query": + if (param.isEmpty()) { + System.out.println("Please enter your query:"); + param = scanner.nextLine(); + } + executeQuery(param); + break; + + default: + System.out.println("Unknown command: " + cmd); + System.out.println("Available commands: test, create, use, remember, query, help, exit"); + } + } + + private void testServer() throws IOException { + System.out.println("Testing server connection..."); + String response = sendGetRequest(SERVER_URL); + System.out.println("✓ Server response: " + response); + } + + private void createGraph(String graphName) throws IOException { + System.out.println("Creating graph: " + graphName); + + GraphSchema testGraph = new GraphSchema(); + testGraph.setName(graphName); + String graphJson = gson.toJson(testGraph); + String response = sendPostRequest(CREATE_URL, graphJson); + System.out.println("✓ Graph created: " + response); + + Map params = new HashMap<>(); + params.put("graphName", graphName); + VertexSchema vertexSchema = new VertexSchema(VERTEX_LABEL, Constants.PREFIX_ID, + Collections.singletonList("text")); + response = sendPostRequest(SCHEMA_URL, gson.toJson(vertexSchema), params); + System.out.println("✓ Chunk schema added: " + response); + + EdgeSchema edgeSchema = new EdgeSchema(EDGE_LABEL, Constants.PREFIX_SRC_ID, Constants.PREFIX_DST_ID, + Collections.singletonList("rel")); + response = sendPostRequest(SCHEMA_URL, gson.toJson(edgeSchema), params); + System.out.println("✓ Relation schema added: " + response); + + System.out.println("✓ Graph '" + graphName + "' is ready for use!"); + } + + private void rememberContent(String content) throws IOException { + if (currentGraphName == null) { + System.out.println("No graph selected. Please create a graph first."); + return; + } + + if (content.trim().toLowerCase(Locale.ROOT).startsWith("doc")) { + String path = content.trim().substring(3).trim(); + + TextFileReader textFileReader = new TextFileReader(10000); + textFileReader.readFile(path); + List chunks = IntStream.range(0, textFileReader.getRowCount()) + .mapToObj(textFileReader::getRow) + .map(String::trim).collect(Collectors.toList()); + for (String chunk : chunks) { + String response = rememberChunk(chunk); + System.out.println("✓ Content remembered: " + response); + } + } + + System.out.println("Remembering content..."); + String response = rememberChunk(content); + System.out.println("✓ Content remembered: " + response); + + } + + + private String rememberChunk(String content) throws IOException { + String vertexId = "chunk_" + System.currentTimeMillis() + "_" + Math.abs(content.hashCode()); + Vertex chunkVertex = new Vertex("chunk", vertexId, Collections.singletonList(content)); + String vertexJson = gson.toJson(chunkVertex); + + Map params = new HashMap<>(); + params.put("graphName", currentGraphName); + + return sendPostRequest(INSERT_URL, vertexJson, params); + } + + private void executeQuery(String query) throws IOException { + if (currentGraphName == null) { + System.out.println("No graph selected. Please create a graph first."); + return; + } + + System.out.println("Creating new session..."); + Map params = new HashMap<>(); + params.put("graphName", currentGraphName); + String response = sendPostRequest(CONTEXT_URL, "", params); + currentSessionId = response.trim(); + System.out.println("✓ Session created: " + currentSessionId); + + System.out.println("Executing query: " + query); + + params = new HashMap<>(); + params.put("sessionId", currentSessionId); + + response = sendPostRequest(EXEC_URL, query, params); + System.out.println("✓ Query result:"); + System.out.println("========================"); + System.out.println(response); + System.out.println("========================"); + } + + private String sendGetRequest(String urlStr) throws IOException { + URL url = new URL(urlStr); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Accept", "application/json"); + + int responseCode = conn.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + throw new IOException("HTTP error code: " + responseCode); + } + + try (BufferedReader br = new BufferedReader( + new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + StringBuilder response = new StringBuilder(); + String responseLine; + while ((responseLine = br.readLine()) != null) { + response.append(responseLine.trim()); + } + return response.toString(); + } + } + + private String sendPostRequest(String urlStr, String body) throws IOException { + return sendPostRequest(urlStr, body, Collections.emptyMap()); + } + + private String sendPostRequest(String urlStr, String body, Map queryParams) throws IOException { + if (!queryParams.isEmpty()) { + StringBuilder urlBuilder = new StringBuilder(urlStr); + urlBuilder.append("?"); + boolean first = true; + for (Map.Entry entry : queryParams.entrySet()) { + if (!first) { + urlBuilder.append("&"); + } + urlBuilder.append(URLEncoder.encode(entry.getKey(), "UTF-8")); + urlBuilder.append("="); + urlBuilder.append(URLEncoder.encode(entry.getValue(), "UTF-8")); + first = false; + } + urlStr = urlBuilder.toString(); + } + + URL url = new URL(urlStr); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Accept", "application/json"); + conn.setDoOutput(true); + + try (OutputStream os = conn.getOutputStream()) { + byte[] input = body.getBytes(StandardCharsets.UTF_8); + os.write(input, 0, input.length); + } + + int responseCode = conn.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + String errorMessage = readErrorResponse(conn); + throw new IOException("HTTP error code: " + responseCode + ", Message: " + errorMessage); + } + + try (BufferedReader br = new BufferedReader( + new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + StringBuilder response = new StringBuilder(); + String responseLine; + while ((responseLine = br.readLine()) != null) { + response.append(responseLine); + } + return response.toString(); + } + } + + private String readErrorResponse(HttpURLConnection conn) throws IOException { + try (BufferedReader br = new BufferedReader( + new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8))) { + StringBuilder response = new StringBuilder(); + String responseLine; + while ((responseLine = br.readLine()) != null) { + response.append(responseLine); + } + return response.toString(); + } catch (Exception e) { + return "No error message available"; + } + } + + private void printWelcome() { + System.out.println("========================================="); + System.out.println(" GeaFlow Memory Server - Simple Client"); + System.out.println("========================================="); + System.out.println("Simple Commands:"); + System.out.println(" test - Test server connection"); + System.out.println(" create [name] - Create a new memory graph"); + System.out.println(" use [name] - Use a new memory graph"); + System.out.println(" remember - Store content to memory"); + System.out.println(" query - Ask questions about memory"); + System.out.println(" help - Show this help"); + System.out.println(" exit - Quit the client"); + System.out.println("========================================="); + System.out.println("Default graph name: " + DEFAULT_GRAPH_NAME); + System.out.println("Server URL: " + BASE_URL); + System.out.println("========================================="); + } + + private void printHelp() { + System.out.println("\nAvailable Commands:"); + System.out.println("-------------------"); + System.out.println("test"); + System.out.println(" Test if the GeaFlow server is running"); + System.out.println(" Example: test"); + System.out.println(); + System.out.println("create [graph_name]"); + System.out.println(" Create a new memory graph with default schema"); + System.out.println(" Creates: chunk vertices and relation edges"); + System.out.println(" Default name: " + DEFAULT_GRAPH_NAME); + System.out.println(" Example: create"); + System.out.println(" Example: create my_memory"); + System.out.println(); + System.out.println("use [graph_name]"); + System.out.println(" Use a new memory graph"); + System.out.println(" Default name: " + DEFAULT_GRAPH_NAME); + System.out.println(" Example: use my_memory"); + System.out.println(); + System.out.println("remember "); + System.out.println(" Store text content into memory"); + System.out.println(" Creates a 'chunk' vertex with the content"); + System.out.println(" Example: remember \"孔子是中国古代的思想家\""); + System.out.println(" Example: remember"); + System.out.println(" (will prompt for content)"); + System.out.println(); + System.out.println("query "); + System.out.println(" Query the memory with natural language"); + System.out.println(" Example: query \"Who is Confucius?\""); + System.out.println(" Example: query"); + System.out.println(" (will prompt for question)"); + System.out.println(); + System.out.println("exit / quit"); + System.out.println(" Exit the client"); + } +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/ErrorCode.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/ErrorCode.java index 34125de39..41eed3f08 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/ErrorCode.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/ErrorCode.java @@ -27,4 +27,6 @@ public class ErrorCode { public static final int GRAPH_ENTITY_GROUP_INSERT_FAILED = 100003; public static final int GRAPH_ENTITY_GROUP_UPDATE_FAILED = 100004; public static final int GRAPH_ENTITY_GROUP_REMOVE_FAILED = 100005; + public static final int GRAPH_ADD_VERTEX_SCHEMA_FAILED = 100006; + public static final int GRAPH_ADD_EDGE_SCHEMA_FAILED = 100007; } diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/config/Constants.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/config/Constants.java index 676ab0810..ef2646035 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/config/Constants.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/config/Constants.java @@ -26,6 +26,9 @@ public class Constants { public static String PREFIX_E = "E"; public static String PREFIX_GRAPH = "GRAPH"; public static String PREFIX_TMP_SESSION = "TmpSession-"; + public static String PREFIX_SRC_ID = "srcId"; + public static String PREFIX_DST_ID = "dstId"; + public static String PREFIX_ID = "id"; public static int HTTP_CALL_TIMEOUT_SECONDS = 300; public static int HTTP_CONNECT_TIMEOUT_SECONDS = 300; @@ -43,4 +46,7 @@ public class Constants { public static double EMBEDDING_OPERATE_DEFAULT_THRESHOLD = 0.5; public static int EMBEDDING_OPERATE_DEFAULT_TOPN = 50; public static int GRAPH_SEARCH_STORE_DEFAULT_TOPN = 30; + + public static String CONSOLIDATE_KEYWORD_RELATION_LABEL = "consolidate_keyword_edge"; + public static String PREFIX_COMMON_KEYWORDS = "common_keywords"; } diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/util/SeDeUtil.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/util/SeDeUtil.java new file mode 100644 index 000000000..af05a03c1 --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/common/util/SeDeUtil.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai.common.util; + +import com.google.gson.*; +import java.util.ArrayList; +import java.util.List; +import org.apache.geaflow.ai.common.config.Constants; +import org.apache.geaflow.ai.graph.GraphEdge; +import org.apache.geaflow.ai.graph.GraphEntity; +import org.apache.geaflow.ai.graph.GraphVertex; +import org.apache.geaflow.ai.graph.io.*; + +public class SeDeUtil { + + private static final Gson GSON = new Gson(); + + public static String serializeGraphSchema(GraphSchema schema) { + return GSON.toJson(schema); + } + + public static GraphSchema deserializeGraphSchema(String json) { + return GSON.fromJson(json, GraphSchema.class); + } + + public static Schema deserializeEntitySchema(String json) { + boolean isVertex = new JsonParser().parse(json).getAsJsonObject().has("idField"); + if (isVertex) { + return GSON.fromJson(json, VertexSchema.class); + } else { + return GSON.fromJson(json, EdgeSchema.class); + } + } + + public static String serializeEntitySchema(Schema schema) { + if (schema instanceof VertexSchema) { + return GSON.toJson((VertexSchema) schema); + } else { + return GSON.toJson((EdgeSchema) schema); + } + } + + public static List deserializeEntities(String json) { + JsonArray jsonArray; + List entities = new ArrayList<>(); + try { + jsonArray = new JsonParser().parse(json).getAsJsonArray(); + } catch (Throwable e) { + JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject(); + if (jsonObject.has(Constants.PREFIX_ID)) { + entities.add(new GraphVertex(GSON.fromJson(json, Vertex.class))); + } else { + entities.add(new GraphEdge(GSON.fromJson(json, Edge.class))); + } + return entities; + } + for (JsonElement element : jsonArray) { + JsonObject jsonObject = element.getAsJsonObject(); + if (jsonObject.has(Constants.PREFIX_ID)) { + entities.add(new GraphVertex(GSON.fromJson(json, Vertex.class))); + } else { + entities.add(new GraphEdge(GSON.fromJson(json, Edge.class))); + } + } + return entities; + } + +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/ConsolidateServer.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/ConsolidateServer.java new file mode 100644 index 000000000..d446b6515 --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/ConsolidateServer.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai.consolidate; + +import java.util.ArrayList; +import java.util.List; +import org.apache.geaflow.ai.consolidate.function.ConsolidateFunction; +import org.apache.geaflow.ai.consolidate.function.EmbeddingRelationFunction; +import org.apache.geaflow.ai.consolidate.function.KeywordRelationFunction; +import org.apache.geaflow.ai.graph.GraphAccessor; +import org.apache.geaflow.ai.graph.MutableGraph; + +public class ConsolidateServer { + + private static final List functions = new ArrayList<>(); + + static { + functions.add(new KeywordRelationFunction()); + functions.add(new EmbeddingRelationFunction()); + } + + public int executeConsolidateTask(GraphAccessor graphAccessor, MutableGraph graph) { + for (ConsolidateFunction function : functions) { + function.eval(graphAccessor, graph); + } + return 0; + } +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/ConsolidateFunction.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/ConsolidateFunction.java new file mode 100644 index 000000000..9dabdb64f --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/ConsolidateFunction.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai.consolidate.function; + +import org.apache.geaflow.ai.graph.GraphAccessor; +import org.apache.geaflow.ai.graph.MutableGraph; + +public interface ConsolidateFunction { + + void eval(GraphAccessor graphAccessor, MutableGraph graph); +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/EmbeddingRelationFunction.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/EmbeddingRelationFunction.java new file mode 100644 index 000000000..34976ef9c --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/EmbeddingRelationFunction.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai.consolidate.function; + +import org.apache.geaflow.ai.graph.GraphAccessor; +import org.apache.geaflow.ai.graph.MutableGraph; + +public class EmbeddingRelationFunction implements ConsolidateFunction { + + @Override + public void eval(GraphAccessor graphAccessor, MutableGraph graph) { + + } +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/KeywordRelationFunction.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/KeywordRelationFunction.java new file mode 100644 index 000000000..29291eea2 --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/consolidate/function/KeywordRelationFunction.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai.consolidate.function; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import org.apache.geaflow.ai.GraphMemoryServer; +import org.apache.geaflow.ai.common.ErrorCode; +import org.apache.geaflow.ai.common.config.Constants; +import org.apache.geaflow.ai.graph.*; +import org.apache.geaflow.ai.graph.io.Edge; +import org.apache.geaflow.ai.graph.io.EdgeSchema; +import org.apache.geaflow.ai.index.EntityAttributeIndexStore; +import org.apache.geaflow.ai.index.vector.KeywordVector; +import org.apache.geaflow.ai.search.VectorSearch; +import org.apache.geaflow.ai.verbalization.SubgraphSemanticPromptFunction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KeywordRelationFunction implements ConsolidateFunction { + + private static final Logger LOGGER = LoggerFactory.getLogger(KeywordRelationFunction.class); + + @Override + public void eval(GraphAccessor graphAccessor, MutableGraph mutableGraph) { + EntityAttributeIndexStore indexStore = new EntityAttributeIndexStore(); + indexStore.initStore(new SubgraphSemanticPromptFunction(graphAccessor)); + LOGGER.info("Success to init EntityAttributeIndexStore."); + GraphMemoryServer server = new GraphMemoryServer(); + server.addGraphAccessor(graphAccessor); + server.addIndexStore(indexStore); + LOGGER.info("Success to init GraphMemoryServer."); + + if (null == mutableGraph.getSchema().getSchema( + Constants.CONSOLIDATE_KEYWORD_RELATION_LABEL)) { + int code = mutableGraph.addEdgeSchema(new EdgeSchema( + Constants.CONSOLIDATE_KEYWORD_RELATION_LABEL, + Constants.PREFIX_SRC_ID, Constants.PREFIX_DST_ID, + Collections.singletonList(Constants.PREFIX_COMMON_KEYWORDS) + )); + if (code != ErrorCode.SUCCESS) { + return; + } + } + + Long cnt = 0L; + Iterator vertexIterator = graphAccessor.scanVertex(); + while (vertexIterator.hasNext()) { + GraphVertex vertex = vertexIterator.next(); + boolean existRelation = false; + Iterator neighborIterator = graphAccessor.scanEdge(vertex); + while (neighborIterator.hasNext()) { + GraphEdge existEdge = neighborIterator.next(); + if (existEdge.getLabel().equals(Constants.CONSOLIDATE_KEYWORD_RELATION_LABEL)) { + existRelation = true; + break; + } + } + if (existRelation) { + continue; + } + String sessionId = server.createSession(); + VectorSearch search = new VectorSearch(null, sessionId); + search.addVector(new KeywordVector(vertex.toString())); + sessionId = server.search(search); + List results = server.getSessionEntities(sessionId); + for (GraphEntity relateEntity : results) { + if (relateEntity instanceof GraphVertex) { + String srcId = vertex.getVertex().getId(); + String dstId = ((GraphVertex) relateEntity).getVertex().getId(); + mutableGraph.addEdge(new Edge( + Constants.CONSOLIDATE_KEYWORD_RELATION_LABEL, + srcId, dstId, + Collections.singletonList(Constants.PREFIX_COMMON_KEYWORDS) + )); + } + } + + cnt++; + LOGGER.info("Process vertex num: {}", cnt); + } + + } +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/MemoryMutableGraph.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/MemoryMutableGraph.java index 7c9a3a613..37b9a6d93 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/MemoryMutableGraph.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/MemoryMutableGraph.java @@ -19,9 +19,10 @@ package org.apache.geaflow.ai.graph; -import org.apache.geaflow.ai.graph.io.Edge; -import org.apache.geaflow.ai.graph.io.MemoryGraph; -import org.apache.geaflow.ai.graph.io.Vertex; +import java.util.ArrayList; +import org.apache.commons.lang3.StringUtils; +import org.apache.geaflow.ai.common.ErrorCode; +import org.apache.geaflow.ai.graph.io.*; public class MemoryMutableGraph implements MutableGraph { @@ -55,4 +56,55 @@ public int removeEdge(Edge edge) { public int addEdge(Edge newEdge) { return this.graph.addEdge(newEdge); } + + @Override + public GraphSchema getSchema() { + return this.graph.getGraphSchema(); + } + + @Override + public int addVertexSchema(VertexSchema vertexSchema) { + if (vertexSchema == null || StringUtils.isBlank(vertexSchema.getLabel())) { + return ErrorCode.GRAPH_ADD_VERTEX_SCHEMA_FAILED; + } + for (VertexSchema existSchema : this.getSchema().getVertexSchemaList()) { + if (existSchema.getLabel().equals(vertexSchema.getLabel())) { + return ErrorCode.GRAPH_ADD_VERTEX_SCHEMA_FAILED; + } + } + for (EdgeSchema existSchema : this.getSchema().getEdgeSchemaList()) { + if (existSchema.getLabel().equals(vertexSchema.getLabel())) { + return ErrorCode.GRAPH_ADD_VERTEX_SCHEMA_FAILED; + } + } + if (this.graph.entities.get(vertexSchema.getLabel()) != null) { + return ErrorCode.GRAPH_ADD_VERTEX_SCHEMA_FAILED; + } + this.graph.getGraphSchema().addVertex(vertexSchema); + this.graph.entities.put(vertexSchema.getLabel(), new VertexGroup(vertexSchema, new ArrayList<>())); + return ErrorCode.SUCCESS; + } + + @Override + public int addEdgeSchema(EdgeSchema edgeSchema) { + if (edgeSchema == null || StringUtils.isBlank(edgeSchema.getLabel())) { + return ErrorCode.GRAPH_ADD_EDGE_SCHEMA_FAILED; + } + for (VertexSchema existSchema : this.getSchema().getVertexSchemaList()) { + if (existSchema.getLabel().equals(edgeSchema.getLabel())) { + return ErrorCode.GRAPH_ADD_EDGE_SCHEMA_FAILED; + } + } + for (EdgeSchema existSchema : this.getSchema().getEdgeSchemaList()) { + if (existSchema.getLabel().equals(edgeSchema.getLabel())) { + return ErrorCode.GRAPH_ADD_EDGE_SCHEMA_FAILED; + } + } + if (this.graph.entities.get(edgeSchema.getLabel()) != null) { + return ErrorCode.GRAPH_ADD_EDGE_SCHEMA_FAILED; + } + this.graph.getGraphSchema().addEdge(edgeSchema); + this.graph.entities.put(edgeSchema.getLabel(), new EdgeGroup(edgeSchema, new ArrayList<>())); + return ErrorCode.SUCCESS; + } } diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/MutableGraph.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/MutableGraph.java index 8ad84891c..0f999a694 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/MutableGraph.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/MutableGraph.java @@ -19,8 +19,7 @@ package org.apache.geaflow.ai.graph; -import org.apache.geaflow.ai.graph.io.Edge; -import org.apache.geaflow.ai.graph.io.Vertex; +import org.apache.geaflow.ai.graph.io.*; public interface MutableGraph { @@ -33,4 +32,10 @@ public interface MutableGraph { int removeEdge(Edge edge); int addEdge(Edge newEdge); + + GraphSchema getSchema(); + + int addVertexSchema(VertexSchema vertexSchema); + + int addEdgeSchema(EdgeSchema edgeSchema); } diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/io/MemoryGraph.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/io/MemoryGraph.java index 8b8592614..c1ae84b1e 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/io/MemoryGraph.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/io/MemoryGraph.java @@ -57,6 +57,9 @@ public Vertex getVertex(String label, String id) { } } else { VertexGroup vg = (VertexGroup) getEntity(label); + if (vg == null) { + return null; + } return vg.getVertex(id); } return null; @@ -106,6 +109,9 @@ public int addVertex(Vertex newVertex) { @Override public List getEdge(String label, String src, String dst) { EdgeGroup eg = (EdgeGroup) getEntity(label); + if (eg == null) { + return Collections.emptyList(); + } return eg.getEdge(src, dst); } diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/io/TextFileReader.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/io/TextFileReader.java new file mode 100644 index 000000000..2ce06823b --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/graph/io/TextFileReader.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai.graph.io; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TextFileReader { + + private static final Logger LOGGER = LoggerFactory.getLogger(TextFileReader.class); + + List fileContent; + long limit; + + public TextFileReader(long limit) { + this.fileContent = new ArrayList<>(); + this.limit = limit; + } + + public void readFile(String fileName) throws IOException { + InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName); + if (inputStream == null) { + LOGGER.error("Cannot find the file: {} (tried as resource)", fileName); + File file = new File(fileName); + if (file.exists() && file.isFile()) { + try { + inputStream = new FileInputStream(file); + } catch (FileNotFoundException e) { + throw new IOException("Cannot find the file: " + fileName + + " (tried both as resource and as absolute path)", e); + } catch (Throwable e2) { + throw new IOException("Cannot open the file: " + fileName + + " (tried both as resource and as absolute path)", e2); + } + } else { + throw new IOException("Cannot find the file: " + fileName + + " (tried both as resource and as absolute path)"); + } + } + + long count = 0; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + + while ((line = reader.readLine()) != null) { + count++; + if (StringUtils.isNotBlank(line)) { + fileContent.add(line); + } + if (count > limit) { + break; + } + } + } + } + + public List getFileContent() { + return fileContent; + } + + public int getRowCount() { + if (fileContent.isEmpty()) { + return 0; + } + return fileContent.size(); + } + + public String getRow(int rowIndex) { + String row = null; + int rowCount = getRowCount(); + if (rowIndex < 0 || rowIndex >= rowCount) { + throw new IndexOutOfBoundsException("Row index out of range: " + rowIndex); + } + row = fileContent.get(rowIndex); + return row; + } + + public void printContent() { + LOGGER.info("Data content:"); + for (String content : fileContent) { + LOGGER.info(content); + } + LOGGER.info("Total row count: " + getRowCount()); + } +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/operator/GraphSearchStore.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/operator/GraphSearchStore.java index 34cf7440b..20beb48ad 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/operator/GraphSearchStore.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/operator/GraphSearchStore.java @@ -32,6 +32,7 @@ import org.apache.geaflow.ai.index.vector.IVector; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; +import org.apache.lucene.index.IndexNotFoundException; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; @@ -118,6 +119,8 @@ public List search(String key1, GraphAccessor graphAccessor) { } } return result; + } catch (IndexNotFoundException notFoundException) { + return new ArrayList<>(); } catch (Throwable e) { throw new RuntimeException("Cannot read search store", e); } diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/service/ServerMemoryCache.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/service/ServerMemoryCache.java new file mode 100644 index 000000000..8e894d180 --- /dev/null +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/service/ServerMemoryCache.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai.service; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import org.apache.geaflow.ai.GraphMemoryServer; +import org.apache.geaflow.ai.consolidate.ConsolidateServer; +import org.apache.geaflow.ai.graph.Graph; + +public class ServerMemoryCache { + + private final Map name2Graph = new LinkedHashMap<>(); + private final Map name2Server = new LinkedHashMap<>(); + private final Map session2GraphName = new HashMap<>(); + private final ConsolidateServer consolidateServer = new ConsolidateServer(); + + public void putGraph(Graph g) { + name2Graph.put(g.getGraphSchema().getName(), g); + } + + public void putServer(GraphMemoryServer server) { + name2Server.put(server.getGraphAccessors().get(0) + .getGraphSchema().getName(), server); + } + + public void putSession(GraphMemoryServer server, String sessionId) { + session2GraphName.put(sessionId, + server.getGraphAccessors().get(0).getGraphSchema().getName()); + } + + public Graph getGraphByName(String name) { + return name2Graph.get(name); + } + + public GraphMemoryServer getServerByName(String name) { + return name2Server.get(name); + } + + public String getGraphNameBySession(String sessionId) { + return session2GraphName.get(sessionId); + } + + public ConsolidateServer getConsolidateServer() { + return consolidateServer; + } +} diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/session/SessionManagement.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/session/SessionManagement.java index 5c4b4a350..3e991e756 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/session/SessionManagement.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/session/SessionManagement.java @@ -25,11 +25,10 @@ public class SessionManagement { - public static final SessionManagement INSTANCE = new SessionManagement(); private final Map session2ActiveTime = new HashMap<>(); private final Map> session2Graphs = new HashMap<>(); - private SessionManagement() { + public SessionManagement() { } public boolean createSession(String sessionId) { @@ -44,7 +43,11 @@ public boolean createSession(String sessionId) { public String createSession() { String sessionId = Constants.PREFIX_TMP_SESSION + System.nanoTime() + UUID.randomUUID().toString().replace("-", "").substring(0, 8); - return createSession(sessionId) ? sessionId : null; + if (createSession(sessionId)) { + return sessionId; + } else { + return null; + } } public boolean sessionExists(String session) { diff --git a/geaflow-ai/src/main/java/org/apache/geaflow/ai/verbalization/SubgraphSemanticPromptFunction.java b/geaflow-ai/src/main/java/org/apache/geaflow/ai/verbalization/SubgraphSemanticPromptFunction.java index 2aca60c0b..0b3407c06 100644 --- a/geaflow-ai/src/main/java/org/apache/geaflow/ai/verbalization/SubgraphSemanticPromptFunction.java +++ b/geaflow-ai/src/main/java/org/apache/geaflow/ai/verbalization/SubgraphSemanticPromptFunction.java @@ -72,12 +72,12 @@ public List verbalize(GraphEntity entity) { if (entity instanceof GraphVertex) { GraphVertex graphVertex = (GraphVertex) entity; return graphVertex.getVertex().getValues().stream() - .filter(SearchUtils::isAllAllowedChars) + .filter(str -> !SearchUtils.isAllAllowedChars(str)) .map(SearchUtils::formatQuery).collect(Collectors.toList()); } else if (entity instanceof GraphEdge) { GraphEdge graphEdge = (GraphEdge) entity; return graphEdge.getEdge().getValues().stream() - .filter(SearchUtils::isAllAllowedChars) + .filter(str -> !SearchUtils.isAllAllowedChars(str)) .map(SearchUtils::formatQuery).collect(Collectors.toList()); } return new ArrayList<>(); diff --git a/geaflow-ai/src/main/resources/application.yml b/geaflow-ai/src/main/resources/application.yml new file mode 100644 index 000000000..71c8171f7 --- /dev/null +++ b/geaflow-ai/src/main/resources/application.yml @@ -0,0 +1,30 @@ +################################################################################ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +################################################################################ +solon: + app: + name: geaflow-memory-server + group: geaflow + +server: + port: 8080 + host: 0.0.0.0 + +logging: + level: + root: INFO + org.apache.geaflow.ai: DEBUG diff --git a/geaflow-ai/src/test/java/org/apache/geaflow/ai/MemoryServerTest.java b/geaflow-ai/src/test/java/org/apache/geaflow/ai/MemoryServerTest.java new file mode 100644 index 000000000..73db0a1af --- /dev/null +++ b/geaflow-ai/src/test/java/org/apache/geaflow/ai/MemoryServerTest.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.geaflow.ai; + +import com.google.gson.Gson; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import okhttp3.*; +import org.apache.geaflow.ai.common.config.Constants; +import org.apache.geaflow.ai.graph.io.*; +import org.junit.jupiter.api.*; +import org.noear.solon.test.SolonTest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SolonTest(GeaFlowMemoryServer.class) +public class MemoryServerTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(MemoryServerTest.class); + private static final String BASE_URL = "http://localhost:8080"; + private static final String GRAPH_NAME = "Confucius"; + private static OkHttpClient client; + + @BeforeEach + void setUp() { + LOGGER.info("Setting up test environment..."); + if (client == null) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.callTimeout(Constants.HTTP_CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS); + builder.connectTimeout(Constants.HTTP_CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + builder.readTimeout(Constants.HTTP_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS); + builder.writeTimeout(Constants.HTTP_WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + client = builder.build(); + } + LOGGER.info("Test HTTP client initialized, base URL: {}", BASE_URL); + } + + @AfterEach + void tearDown() { + LOGGER.info("Cleaning up test environment..."); + } + + private String get(String useApi) { + String url = BASE_URL + useApi; + Request request = new Request.Builder().url(url).get().build(); + try (okhttp3.Response response = client.newCall(request).execute()) { + return response.body().string(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private String post(String useApi, String bodyJson) { + return post(useApi, bodyJson, Collections.emptyMap()); + } + + private String post(String useApi, String bodyJson, Map queryParams) { + RequestBody requestBody = RequestBody.create( + MediaType.parse("application/json; charset=utf-8"), bodyJson); + String url = BASE_URL + useApi; + HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); + if (queryParams != null) { + for (Map.Entry entry : queryParams.entrySet()) { + urlBuilder.addQueryParameter(entry.getKey(), entry.getValue()); + } + } + Request request = new Request.Builder().url(urlBuilder.build()).post(requestBody).build(); + try (okhttp3.Response response = client.newCall(request).execute()) { + return response.body().string(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + void testMain() throws Exception { + testServerHealth(); + testCreateGraph(); + testAddEntities(); + testQueries(); + } + + void testServerHealth() throws Exception { + LOGGER.info("Testing server health endpoint..."); + String api = "/"; + String response = get(api); + LOGGER.info("API: {} Response: {}", api, response); + api = "/health"; + response = get(api); + LOGGER.info("API: {} Response: {}", api, response); + api = "/api/test"; + response = get(api); + LOGGER.info("API: {} Response: {}", api, response); + } + + void testCreateGraph() throws Exception { + LOGGER.info("Testing server create graph..."); + Gson gson = new Gson(); + String api = "/graph/create"; + GraphSchema testGraph = new GraphSchema(); + String graphName = GRAPH_NAME; + testGraph.setName(graphName); + String response = post(api, gson.toJson(testGraph)); + LOGGER.info("API: {} Response: {}", api, response); + + api = "/graph/getGraphSchema"; + Map queryParams = new HashMap<>(); + queryParams.put("graphName", graphName); + response = post(api, "", queryParams); + LOGGER.info("API: {} Response: {}", api, response); + Assertions.assertEquals(gson.toJson(testGraph), response); + + VertexSchema vertexSchema = new VertexSchema("chunk", "id", + Collections.singletonList("text")); + EdgeSchema edgeSchema = new EdgeSchema("relation", "srcId", "dstId", + Collections.singletonList("rel")); + testGraph.addVertex(vertexSchema); + testGraph.addEdge(edgeSchema); + + api = "/graph/addEntitySchema"; + queryParams = new HashMap<>(); + queryParams.put("graphName", graphName); + response = post(api, gson.toJson(vertexSchema), queryParams); + LOGGER.info("API: {} Response: {}", api, response); + + api = "/graph/addEntitySchema"; + queryParams = new HashMap<>(); + queryParams.put("graphName", graphName); + response = post(api, gson.toJson(edgeSchema), queryParams); + LOGGER.info("API: {} Response: {}", api, response); + + api = "/graph/getGraphSchema"; + queryParams = new HashMap<>(); + queryParams.put("graphName", graphName); + response = post(api, "", queryParams); + LOGGER.info("API: {} Response: {}", api, response); + Assertions.assertEquals(gson.toJson(testGraph), response); + } + + void testAddEntities() throws Exception { + LOGGER.info("Testing server add entities..."); + Gson gson = new Gson(); + String graphName = GRAPH_NAME; + + String api = "/graph/getGraphSchema"; + Map queryParams = new HashMap<>(); + queryParams.put("graphName", graphName); + String response = post(api, "", queryParams); + LOGGER.info("API: {} Response: {}", api, response); + + TextFileReader textFileReader = new TextFileReader(10000); + textFileReader.readFile("text/Confucius"); + List chunks = IntStream.range(0, textFileReader.getRowCount()) + .mapToObj(textFileReader::getRow) + .map(String::trim).collect(Collectors.toList()); + for(String chunk : chunks) { + String vid = UUID.randomUUID().toString().replace("-", ""); + Vertex chunkVertex = new Vertex("chunk", vid, Collections.singletonList(chunk)); + api = "/graph/insertEntity"; + queryParams = new HashMap<>(); + queryParams.put("graphName", graphName); + response = post(api, gson.toJson(chunkVertex), queryParams); + LOGGER.info("API: {} Response: {}", api, response); + } + } + + void testQueries() throws Exception { + LOGGER.info("Testing server queries..."); + String graphName = GRAPH_NAME; + String sessionId = null; + String api = "/query/context"; + Map queryParams = new HashMap<>(); + queryParams.put("graphName", graphName); + String response = post(api, "", queryParams); + LOGGER.info("API: {} Response: {}", api, response); + Assertions.assertNotNull(response); + sessionId = response; + + api = "/query/exec"; + queryParams = new HashMap<>(); + queryParams.put("sessionId", sessionId); + queryParams.put("query", "Who is Confucius?"); + response = post(api, "", queryParams); + LOGGER.info("API: {} Response: {}", api, response); + Assertions.assertNotNull(response); + + api = "/query/result"; + queryParams = new HashMap<>(); + queryParams.put("sessionId", sessionId); + response = post(api, "", queryParams); + LOGGER.info("API: {} Response: {}", api, response); + Assertions.assertNotNull(response); + + api = "/query/exec"; + queryParams = new HashMap<>(); + queryParams.put("sessionId", sessionId); + queryParams.put("query", "What did he say?"); + response = post(api, "", queryParams); + LOGGER.info("API: {} Response: {}", api, response); + Assertions.assertNotNull(response); + + api = "/query/result"; + queryParams = new HashMap<>(); + queryParams.put("sessionId", sessionId); + response = post(api, "", queryParams); + LOGGER.info("API: {} Response: {}", api, response); + Assertions.assertNotNull(response); + } + +} diff --git a/geaflow-ai/src/test/java/org/apache/geaflow/ai/MutableGraphTest.java b/geaflow-ai/src/test/java/org/apache/geaflow/ai/MutableGraphTest.java index dd2463c9c..1a92d7849 100644 --- a/geaflow-ai/src/test/java/org/apache/geaflow/ai/MutableGraphTest.java +++ b/geaflow-ai/src/test/java/org/apache/geaflow/ai/MutableGraphTest.java @@ -19,10 +19,14 @@ package org.apache.geaflow.ai; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.apache.geaflow.ai.common.config.Constants; +import org.apache.geaflow.ai.consolidate.ConsolidateServer; +import org.apache.geaflow.ai.graph.GraphEntity; +import org.apache.geaflow.ai.graph.GraphVertex; import org.apache.geaflow.ai.graph.LocalMemoryGraphAccessor; import org.apache.geaflow.ai.graph.MemoryMutableGraph; import org.apache.geaflow.ai.graph.io.*; @@ -139,4 +143,94 @@ private String searchInGraph(GraphMemoryServer server, assert context != null; return context.toString(); } + + @Test + public void testConsolidation() throws IOException { + GraphSchema graphSchema = new GraphSchema(); + VertexSchema vertexSchema = new VertexSchema("chunk", "id", + Collections.singletonList("text")); + EdgeSchema edgeSchema = new EdgeSchema("relation", "srcId", "dstId", + Collections.singletonList("rel")); + graphSchema.addVertex(vertexSchema); + graphSchema.addEdge(edgeSchema); + Map entities = new HashMap<>(); + entities.put(vertexSchema.getName(), new VertexGroup(vertexSchema, new ArrayList<>())); + entities.put(edgeSchema.getName(), new EdgeGroup(edgeSchema, new ArrayList<>())); + MemoryGraph graph = new MemoryGraph(graphSchema, entities); + + LocalMemoryGraphAccessor graphAccessor = new LocalMemoryGraphAccessor(graph); + MemoryMutableGraph memoryMutableGraph = new MemoryMutableGraph(graph); + LOGGER.info("Success to init empty graph."); + + EntityAttributeIndexStore indexStore = new EntityAttributeIndexStore(); + indexStore.initStore(new SubgraphSemanticPromptFunction(graphAccessor)); + LOGGER.info("Success to init EntityAttributeIndexStore."); + + GraphMemoryServer server = new GraphMemoryServer(); + server.addGraphAccessor(graphAccessor); + server.addIndexStore(indexStore); + LOGGER.info("Success to init GraphMemoryServer."); + + TextFileReader textFileReader = new TextFileReader(10000); + textFileReader.readFile("text/Confucius"); + List chunks = IntStream.range(0, textFileReader.getRowCount()) + .mapToObj(textFileReader::getRow) + .map(String::trim).collect(Collectors.toList()); + for(String chunk : chunks) { + String vid = UUID.randomUUID().toString().replace("-", ""); + memoryMutableGraph.addVertex(new Vertex(vertexSchema.getName(), + vid, Collections.singletonList(chunk))); + } + + GraphVertex vertexForTest = graphAccessor.scanVertex().next(); + String testId = vertexForTest.getVertex().getId(); + String query = vertexForTest.getVertex().getValues().toString(); + List relatedResult = searchGraphEntities(server, query, 3); + Assertions.assertFalse(relatedResult.isEmpty()); + LOGGER.info("query: {} result: {}", query, relatedResult); + + for (GraphEntity relatedEntity : relatedResult) { + if (relatedEntity instanceof GraphVertex) { + String relatedId = ((GraphVertex) relatedEntity).getVertex().getId(); + Assertions.assertTrue(graphAccessor.getEdge( + Constants.CONSOLIDATE_KEYWORD_RELATION_LABEL, testId, relatedId + ).isEmpty()); + } + } + + + ConsolidateServer consolidate = new ConsolidateServer(); + int taskId = consolidate.executeConsolidateTask( + graphAccessor, memoryMutableGraph); + LOGGER.info("Success to run consolidation task, taskId: {}.", taskId); + + relatedResult = searchGraphEntities(server, query, 3); + Assertions.assertFalse(relatedResult.isEmpty()); + LOGGER.info("query: {} result: {}", query, relatedResult); + + //Test for at least one related entity in result + int existNum = 0; + for (GraphEntity relatedEntity : relatedResult) { + if (relatedEntity instanceof GraphVertex) { + String relatedId = ((GraphVertex) relatedEntity).getVertex().getId(); + if(!graphAccessor.getEdge(Constants.CONSOLIDATE_KEYWORD_RELATION_LABEL, + testId, relatedId).isEmpty()) { + existNum++; + } + } + } + LOGGER.info("relatedResult size: {} found size: {}", relatedResult.size(), existNum); + Assertions.assertTrue(existNum > 0); + } + + private List searchGraphEntities(GraphMemoryServer server, + String query, int times) { + String sessionId = server.createSession(); + for (int i = 0; i < times; i++) { + VectorSearch search = new VectorSearch(null, sessionId); + search.addVector(new KeywordVector(query)); + sessionId = server.search(search); + } + return server.getSessionEntities(sessionId); + } } diff --git a/geaflow-ai/src/test/resources/text/Confucius b/geaflow-ai/src/test/resources/text/Confucius new file mode 100644 index 000000000..4e0b4ad0e --- /dev/null +++ b/geaflow-ai/src/test/resources/text/Confucius @@ -0,0 +1,1063 @@ +学而篇第一 + +  子曰:“学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不愠,不亦君子乎?” + +  有子曰:“其为人也孝弟,而好犯上者,鲜矣;不好犯上而好作乱者,未之有也。君子务本,本立而道生。孝弟也者,其为仁之本与!” + +  子曰:“巧言令色,鲜矣仁!” + +  曾子曰:“吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?” + +  子曰:“道千乘之国,敬事而信,节用而爱人,使民以时。” + +  子曰:“弟子入则孝,出则弟,谨而信,泛爱众,而亲仁,行有余力,则以学文。” + +  子夏曰:“贤贤易色;事父母,能竭其力;事君,能致其身;与朋友交,言而有信。虽曰未学,吾必谓之学矣。” + +  子曰:“君子不重则不威,学则不固。主忠信,无友不如己者,过,则勿惮改。” + +  曾子曰:“慎终追远,民德归厚矣。” + +  子禽问于子贡曰:“夫子至于是邦也,必闻其政,求之与,抑与之与?”子贡曰:“夫子温、良、恭、俭、让以得之。夫子之求之也,其诸异乎人之求之与?” + +  子曰:“父在,观其志;父没,观其行;三年无改于父之道,可谓孝矣。” + +  有子曰:“礼之用,和为贵。先王之道,斯为美。小大由之,有所不行。知和而和,不以礼节之,亦不可行也。” + +  有子曰:“信近于义,言可复也。恭近于礼,远耻辱也。因不失其亲,亦可宗也。” + +  子曰:“君子食无求饱,居无求安,敏于事而慎于言,就有道而正焉,可谓好学也已。” + +  子贡曰:“贫而无谄,富而无骄,何如?”子曰:“可也。未若贫而乐,富而好礼者也。”子贡曰:“《诗》云:‘如切如磋,如琢如磨’,其斯之谓与?”子曰:“赐也,始可与言《诗》已矣,告诸往而知来者。” + +  子曰:“不患人之不己知,患不知人也。” + +为政篇第二 + +  子曰:“为政以德,譬如北辰,居其所而众星共之。” + +  子曰:“《诗》三百,一言以蔽之,曰:‘思无邪’。” + +  子曰:“道之以政,齐之以刑,民免而无耻。道之以德,齐之以礼,有耻且格。” + +  子曰:“吾十有五而志于学,三十而立,四十而不惑,五十而知天命,六十而耳顺,七十而从心所欲,不逾矩。” + +  孟懿子问孝,子曰:“无违。”樊迟御,子告之曰:“孟孙问孝于我,我对曰‘无违’。”樊迟曰:“何谓也?”子曰:“生,事之以礼;死,葬之以礼,祭之以礼。” + +  孟武伯问孝。子曰:“父母唯其疾之忧。” + +  子游问孝。子曰:“今之孝者,是谓能养。至于犬马,皆能有养;不敬,何以别乎?” + +  子夏问孝。子曰:“色难。有事,弟子服其劳;有酒食,先生馔,曾是以为孝乎?” + +  子曰:“吾与回言终日,不违,如愚。退而省其私,亦足以发,回也不愚。” + +  子曰:“视其所以,观其所由,察其所安,人焉廋哉?人焉廋哉?” + +  子曰:“温故而知新,可以为师矣。” + +  子曰:“君子不器。” + +  子贡问君子。子曰:“先行其言而后从之。” + +  子曰:“君子周而不比,小人比而不周。” + +  子曰:“学而不思则罔,思而不学则殆。” + +  子曰:“攻乎异端,斯害也已!” + +  子曰:“由,诲女知之乎!知之为知之,不知为不知,是知也。” + +  子张学干禄。子曰:“多闻阙疑,慎言其余,则寡尤;多见阙殆,慎行其余,则寡悔。言寡尤,行寡悔,禄在其中矣。” + +  哀公问曰:“何为则民服?”孔子对曰:“举直错诸枉,则民服;举枉错诸直,则民不服。” + +  季康子问:“使民敬、忠以劝,如之何?”子曰:“临之以庄,则敬;孝慈,则忠;举善而教不能,则劝。” + +  或谓孔子曰:“子奚不为政?”子曰:“《书》云:‘孝乎惟孝,友于兄弟,施于有政。’是亦为政,奚其为为政?” + +  子曰:“人而无信,不知其可也。大车无輗,小车无軏,其何以行之哉?” + +  子张问:“十世可知也?”子曰:“殷因于夏礼,所损益,可知也;周因于殷礼,所损益,可知也。其或继周者,虽百世,可知也。” + +  子曰:“非其鬼而祭之,谄也;见义不为,无勇也。” + +八佾篇第三 + +  孔子谓季氏:“八佾舞于庭,是可忍也,孰不可忍也?” + +  三家者以《雍》彻,子曰:“‘相维辟公,天子穆穆’,奚取于三家之堂?” + +  子曰:“人而不仁,如礼何?人而不仁,如乐何?” + +  林放问礼之本,子曰:“大哉问!礼,与其奢也,宁俭;丧,与其易也,宁戚。” + +  子曰:“夷狄之有君,不如诸夏之亡也。” + +  季氏旅于泰山。子谓冉有曰:“女弗能救与?”对曰:“不能。”子曰:“呜呼!曾谓泰山不如林放乎?” + +  子曰:“君子无所争,必也射乎!揖让而升,下而饮。其争也君子。” + +  子夏问曰:“‘巧笑倩兮,美目盼兮,素以为绚兮’何谓也?”子曰:“绘事后素。”曰:“礼后乎?”子曰:“起予者商也,始可与言《诗》已矣。” + +  子曰:“夏礼吾能言之,杞不足征也;殷礼吾能言之,宋不足征也。文献不足故也,足则吾能征之矣。” + +  子曰:“禘自既灌而往者,吾不欲观之矣。” + +  或问禘之说。子曰:“不知也。知其说者之于天下也,其如示诸斯乎!”指其掌。 + +  祭如在,祭神如神在。子曰:“吾不与祭,如不祭。” + +  王孙贾问曰:“‘与其媚于奥,宁媚于灶’,何谓也?”子曰:“不然,获罪于天,无所祷也。” + +  子曰:“周监于二代,郁郁乎文哉!吾从周。” + +  子入太庙,每事问。或曰:“孰谓鄹人之子知礼乎?入太庙,每事问。”子闻之,曰:“是礼也。” + +  子曰:“射不主皮,为力不同科,古之道也。” + +  子贡欲去告朔之饩羊,子曰:“赐也!尔爱其羊,我爱其礼。” + +  子曰:“事君尽礼,人以为谄也。” + +  定公问:“君使臣,臣事君,如之何?”孔子对曰:“君使臣以礼,臣事君以忠。” + +  子曰:“《关雎》,乐而不淫,哀而不伤。” + +  哀公问社于宰我,宰我对曰:“夏后氏以松,殷人以柏,周人以栗,曰使民战栗。”子闻之,曰:“成事不说,遂事不谏,既往不咎。” + +  子曰:“管仲之器小哉!”或曰:“管仲俭乎?”曰:“管氏有三归,官事不摄,焉得俭?”“然则管仲知礼乎?”曰:“邦君树塞门,管氏亦树塞门;邦君为两君之好,有反坫。管氏亦有反坫,管氏而知礼,孰不知礼?” + +  子语鲁大师乐,曰:“乐其可知也。始作,翕如也;从之,纯如也,皦如也,绎如也,以成。” + +  仪封人请见,曰:“君子之至于斯也,吾未尝不得见也。”从者见之。出曰:“二三子何患于丧乎?天下之无道也久矣,天将以夫子为木铎。” + +  子谓《韶》:“尽美矣,又尽善也。”谓《武》:“尽美矣,未尽善也。” + +  子曰:“居上不宽,为礼不敬,临丧不哀,吾何以观之哉?” + +里仁篇第四 + +  子曰:“里仁为美,择不处仁,焉得知?” + +  子曰:“不仁者不可以久处约,不可以长处乐。仁者安仁,知者利仁。” + +  子曰:“唯仁者能好人,能恶人。” + +  子曰:“苟志于仁矣,无恶也。” + +  子曰:“富与贵,是人之所欲也;不以其道得之,不处也。贫与贱,是人之所恶也;不以其道得之,不去也。君子去仁,恶乎成名?君子无终食之间违仁,造次必于是,颠沛必于是。” + +  子曰:“我未见好仁者,恶不仁者。好仁者,无以尚之;恶不仁者,其为仁矣,不使不仁者加乎其身,有能一日用其力于仁矣乎?我未见力不足者。盖有之矣,我未之见也。” + +  子曰:“人之过也,各于其党。观过,斯知仁矣。” + +  子曰:“朝闻道,夕死可矣。” + +  子曰:“士志于道,而耻恶衣恶食者,未足与议也。” + +  子曰:“君子之于天下也,无适也,无莫也,义之与比。” + +  子曰:“君子怀德,小人怀土;君子怀刑,小人怀惠。” + +  子曰:“放于利而行,多怨。” + +  子曰:“能以礼让为国乎?何有?不能以礼让为国,如礼何?” + +  子曰:“不患无位,患所以立;不患莫己知,求为可知也。” + +  子曰:“参乎!吾道一以贯之。”曾子曰:“唯。”子出,门人问曰:“何谓也?”曾子曰:“夫子之道,忠恕而已矣。” + +  子曰:“君子喻于义,小人喻于利。” + +  子曰:“见贤思齐焉,见不贤而内自省也。” + +  子曰:“事父母几谏,见志不从,又敬不违,劳而不怨。” + +  子曰:“父母在,不远游,游必有方。” + +  子曰:“三年无改于父之道,可谓孝矣。” + +  子曰:“父母之年,不可不知也。一则以喜,一则以惧。” + +  子曰:“古者言之不出,耻躬之不逮也。” + +  子曰:“以约失之者鲜矣。” + +  子曰:“君子欲讷于言而敏于行。” + +  子曰:“德不孤,必有邻。” + +  子游曰:“事君数,斯辱矣;朋友数,斯疏矣。” + +公冶长篇第五 + +  子谓公冶长:“可妻也,虽在缧绁之中,非其罪也!”以其子妻之。 + +  子谓南容:“邦有道不废;邦无道免于刑戮。”以其兄之子妻之。 + +  子谓子贱:“君子哉若人!鲁无君子者,斯焉取斯?” + +  子贡问曰:“赐也何如?”子曰:“女器也。”曰:“何器也?”曰:“瑚琏也。” + +  或曰:“雍也仁而不佞。”子曰:“焉用佞?御人以口给,屡憎于人。不知其仁,焉用佞?” + +  子使漆雕开仕,对曰:“吾斯之未能信。”子说。 + +  子曰:“道不行,乘桴浮于海,从我者其由与?”子路闻之喜,子曰:“由也好勇过我,无所取材。” + +  孟武伯问:“子路仁乎?”子曰:“不知也。”又问,子曰:“由也,千乘之国,可使治其赋也,不知其仁也。”“求也何如?”子曰:“求也,千室之邑、百乘之家,可使为之宰也,不知其仁也。”“赤也何如?”子曰:“赤也,束带立于朝,可使与宾客言也,不知其仁也。” + +  子谓子贡曰:“女与回也孰愈?”对曰:“赐也何敢望回?回也闻一以知十,赐也闻一以知二。”子曰:“弗如也,吾与女弗如也!” + +  宰予昼寝,子曰:“朽木不可雕也,粪土之墙不可杇也,于予与何诛?”子曰:“始吾于人也,听其言而信其行;今吾于人也,听其言而观其行。于予与改是。” + +  子曰:“吾未见刚者。”或对曰:“申枨。”子曰:“枨也欲,焉得刚?” + +  子贡曰:“我不欲人之加诸我也,吾亦欲无加诸人。”子曰:“赐也,非尔所及也。” + +  子贡曰:“夫子之文章,可得而闻也;夫子之言性与天道,不可得而闻也。” + +  子路有闻,未之能行,唯恐有闻。 + +  子贡问曰:“孔文子何以谓之文也?”子曰:“敏而好学,不耻下问,是以谓之文也。” + +  子谓子产:“有君子之道四焉:其行己也恭,其事上也敬,其养民也惠,其使民也义。” + +  子曰:“晏平仲善与人交,久而敬之。” + +  子曰:“臧文仲居蔡,山节藻棁,何如其知也?” + +  子张问曰:“令尹子文三仕为令尹,无喜色,三已之无愠色,旧令尹之政必以告新令尹,何如?”子曰:“忠矣。”曰:“仁矣乎?”曰:“未知,焉得仁?”“崔子弑齐君,陈文子有马十乘,弃而违之。至于他邦,则曰:‘犹吾大夫崔子也。’违之。之一邦,则又曰:‘犹吾大夫崔子也。’违之,何如?”子曰:“清矣。”曰:“仁矣乎?”曰:“未知,焉得仁?” + +  季文子三思而后行,子闻之曰:“再,斯可矣。” + +  子曰:“宁武子,邦有道则知,邦无道则愚。其知可及也,其愚不可及也。” + +  子在陈,曰:“归与!归与!吾党之小子狂简,斐然成章,不知所以裁之。” + +  子曰:“伯夷、叔齐不念旧恶,怨是用希。” + +  子曰:“孰谓微生高直?或乞醯焉,乞诸其邻而与之。” + +  子曰:“巧言、令色、足恭,左丘明耻之,丘亦耻之。匿怨而友其人,左丘明耻之,丘亦耻之。” + +  颜渊、季路侍,子曰:“盍各言尔志?”子路曰:“愿车马衣轻裘,与朋友共,敝之而无憾。”颜渊曰:“愿无伐善,无施劳。”子路曰:“愿闻子之志。”子曰:“老者安之,朋友信之,少者怀之。” + +  子曰:“已矣乎!吾未见能见其过而内自讼者也。” + +  子曰:“十室之邑,必有忠信如丘者焉,不如丘之好学也。” + +雍也篇第六 + +  子曰:“雍也可使南面。” + +  仲弓问子桑伯子,子曰:“可也,简。”仲弓曰:“居敬而行简,以临其民,不亦可乎?居简而行简,无乃大简乎?”子曰:“雍之言然。” + +  哀公问:“弟子孰为好学?”孔子对曰:“有颜回者好学,不迁怒,不贰过,不幸短命死矣,今也则亡,未闻好学者也。” + +  子华使于齐,冉子为其母请粟,子曰:“与之釜。”请益,曰:“与之庾。”冉子与之粟五秉。子曰:“赤之适齐也,乘肥马,衣轻裘。吾闻之也,君子周急不继富。” + +  原思为之宰,与之粟九百,辞。子曰:“毋,以与尔邻里乡党乎!” + +  子谓仲弓曰:“犁牛之子骍且角,虽欲勿用,山川其舍诸?” + +  子曰:“回也,其心三月不违仁,其余则日月至焉而已矣。” + +  季康子问:“仲由可使从政也与?”子曰:“由也果,于从政乎何有?”曰:“赐也可使从政也与?”曰:“赐也达,于从政乎何有?”曰:“求也可使从政也与?”曰:“求也艺,于从政乎何有?” + +  季氏使闵子骞为费宰,闵子骞曰:“善为我辞焉。如有复我者,则吾必在汶上矣。” + +  伯牛有疾,子问之,自牖执其手,曰:“亡之,命矣夫!斯人也而有斯疾也!斯人也而有斯疾也!” + +  子曰:“贤哉回也!一箪食,一瓢饮,在陋巷,人不堪其忧,回也不改其乐。贤哉,回也!” + +  冉求曰:“非不说子之道,力不足也。”子曰:“力不足者,中道而废,今女画。” + +  子谓子夏曰:“女为君子儒,无为小人儒。” + +  子游为武城宰,子曰:“女得人焉尔乎?”曰:“有澹台灭明者,行不由径,非公事,未尝至于偃之室也。” + +  子曰:“孟之反不伐,奔而殿,将入门,策其马曰:‘非敢后也,马不进也。’” + +  子曰:“不有祝鮀之佞,而有宋朝之美,难乎免于今之世矣。” + +  子曰:“谁能出不由户?何莫由斯道也?” + +  子曰:“质胜文则野,文胜质则史。文质彬彬,然后君子。” + +  子曰:“人之生也直,罔之生也幸而免。” + +  子曰:“知之者不如好之者,好之者不如乐之者。” + +  子曰:“中人以上,可以语上也;中人以下,不可以语上也。” + +  樊迟问知,子曰:“务民之义,敬鬼神而远之,可谓知矣。”问仁,曰:“仁者先难而后获,可谓仁矣。” + +  子曰:“知者乐水,仁者乐山。知者动,仁者静。知者乐,仁者寿。” + +  子曰:“齐一变至于鲁,鲁一变至于道。” + +  子曰:“觚不觚,觚哉!觚哉!” + +  宰我问曰:“仁者,虽告之曰:‘井有仁焉。’其从之也?”子曰:“何为其然也?君子可逝也,不可陷也;可欺也,不可罔也。” + +  子曰:“君子博学于文,约之以礼,亦可以弗畔矣夫。” + +  子见南子,子路不说,夫子矢之曰:“予所否者,天厌之!天厌之!” + +  子曰:“中庸之为德也,其至矣乎!民鲜久矣。” + +  子贡曰:“如有博施于民而能济众,何如?可谓仁乎?”子曰:“何事于仁,必也圣乎!尧、舜其犹病诸!夫仁者,己欲立而立人,己欲达而达人。能近取譬,可谓仁之方也已。” + +述而篇第七 + +  子曰:“述而不作,信而好古,窃比于我老彭。” + +  子曰:“默而识之,学而不厌,诲人不倦,何有于我哉?” + +  子曰:“德之不修,学之不讲,闻义不能徙,不善不能改,是吾忧也。” + +  子之燕居,申申如也,夭夭如也。 + +  子曰:“甚矣吾衰也!久矣吾不复梦见周公。” + +  子曰:“志于道,据于德,依于仁,游于艺。” + +  子曰:“自行束脩以上,吾未尝无诲焉。” + +  子曰:“不愤不启,不悱不发,举一隅不以三隅反,则不复也。” + +  子食于有丧者之侧,未尝饱也。 + +  子于是日哭,则不歌。 + +  子谓颜渊曰:“用之则行,舍之则藏,惟我与尔有是夫!”子路曰:“子行三军,则谁与?”子曰:“暴虎冯河,死而无悔者,吾不与也。必也临事而惧,好谋而成者也。” + +  子曰:“富而可求也,虽执鞭之士,吾亦为之。如不可求,从吾所好。” + +  子之所慎:齐(斋),战,疾。 + +  子在齐闻《韶》,三月不知肉味,曰:“不图为乐之至於斯也。” + +  冉有曰:“夫子为卫君乎?”子贡曰:“诺,吾将问之。”入,曰:“伯夷、叔齐何人也?”曰:“古之贤人也。”曰:“怨乎?”曰:“求仁而得仁,又何怨?”出,曰:“夫子不为也。” + +  子曰:“饭疏食饮水,曲肱而枕之,乐亦在其中矣。不义而富且贵,于我如浮云。” + +  子曰:“加我数年,五十以学《易》,可以无大过矣。” + +  子所雅言,《诗》、《书》、执礼,皆雅言也。 + +  叶公问孔子于子路,子路不对。子曰:“女奚不曰:其为人也,发愤忘食,乐以忘忧,不知老之将至云尔。” + +  子曰:“我非生而知之者,好古,敏以求之者也。” + +  子不语怪、力、乱、神。 + +  子曰:“三人行,必有我师焉。择其善者而从之,其不善者而改之。” + +  子曰:“天生德于予,桓魋其如予何?” + +  子曰:“二三子以我为隐乎?吾无隐乎尔!吾无行而不与二三子者,是丘也。” + +  子以四教:文,行,忠,信。 + +  子曰:“圣人,吾不得而见之矣;得见君子者斯可矣。”子曰:“善人,吾不得而见之矣,得见有恒者斯可矣。亡而为有,虚而为盈,约而为泰,难乎有恒矣。” + +  子钓而不纲,弋不射宿。 + +  子曰:“盖有不知而作之者,我无是也。多闻,择其善者而从之;多见而识之,知之次也。” + +  互乡难与言,童子见,门人惑。子曰:“与其进也,不与其退也,唯何甚?人洁己以进,与其洁也,不保其往也。” + +  子曰:“仁远乎哉?我欲仁,斯仁至矣。” + +  陈司败问:“昭公知礼乎?”孔子曰:“知礼。”孔子退,揖巫马期而进之,曰:“吾闻君子不党,君子亦党乎?君取于吴,为同姓,谓之吴孟子。君而知礼,孰不知礼?”巫马期以告,子曰:“丘也幸,苟有过,人必知之。” + +  子与人歌而善,必使反之,而后和之。 + +  子曰:“文,莫吾犹人也。躬行君子,则吾未之有得。” + +  子曰:“若圣与仁,则吾岂敢?抑为之不厌,诲人不倦,则可谓云尔已矣。”公西华曰:“正唯弟子不能学也。” + +  子疾病,子路请祷。子曰:“有诸?”子路对曰:“有之。《诔》曰:‘祷尔于上下神祇。’”子曰:“丘之祷久矣。” + +  子曰:“奢则不孙,俭则固。与其不孙也,宁固。” + +  子曰:“君子坦荡荡,小人长戚戚。” + +  子温而厉,威而不猛,恭而安。 + +泰伯篇第八 + +  子曰:“泰伯,其可谓至德也已矣。三以天下让,民无得而称焉。” + +  子曰:“恭而无礼则劳;慎而无礼则葸;勇而无礼则乱;直而无礼则绞。君子笃于亲,则民兴于仁;故旧不遗,则民不偷。” + +  曾子有疾,召门弟子曰:“启予足,启予手。《诗》云:‘战战兢兢,如临深渊,如履薄冰。’而今而后,吾知免夫,小子!” + +  曾子有疾,孟敬子问之。曾子言曰:“鸟之将死,其鸣也哀;人之将死,其言也善。君子所贵乎道者三:动容貌,斯远暴慢矣;正颜色,斯近信矣;出辞气,斯远鄙倍矣。笾豆之事,则有司存。” + +  曾子曰:“以能问于不能;以多问于寡;有若无,实若虚,犯而不校。昔者吾友尝从事于斯矣。” + +  曾子曰:“可以托六尺之孤,可以寄百里之命,临大节而不可夺也。君子人与?君子人也。” + +  曾子曰:“士不可以不弘毅,任重而道远。仁以为己任,不亦重乎?死而后已,不亦远乎?” + +  子曰:“兴于《诗》,立于礼,成于乐。” + +  子曰:“民可使由之,不可使知之。” + +  子曰:“好勇疾贫,乱也。人而不仁,疾之已甚,乱也。” + +  子曰:“如有周公之才之美,使骄且吝,其余不足观也已。” + +  子曰:“三年学,不至于谷,不易得也。” + +  子曰:“笃信好学,守死善道。危邦不入,乱邦不居。天下有道则见,无道则隐。邦有道,贫且贱焉,耻也;邦无道,富且贵焉,耻也。” + +  子曰:“不在其位,不谋其政。” + +  子曰:“师挚之始,《关雎》之乱,洋洋乎盈耳哉!” + +  子曰:“狂而不直,侗而不愿,悾悾而不信,吾不知之矣。” + +  子曰:“学如不及,犹恐失之。” + +  子曰:“巍巍乎!舜、禹之有天下也而不与焉。” + +  子曰:“大哉尧之为君也!巍巍乎,唯天为大,唯尧则之。荡荡乎,民无能名焉。巍巍乎其有成功也,焕乎其有文章!” + +  舜有臣五人而天下治。武王曰:“予有乱臣十人。”孔子曰:“才难,不其然乎?唐虞之际,于斯为盛;有妇人焉,九人而已。三分天下有其二,以服事殷。周之德,其可谓至德也已矣。” + +  子曰:“禹,吾无间然矣。菲饮食而致孝乎鬼神,恶衣服而致美乎黼冕,卑宫室而尽力乎沟洫。禹,吾无间然矣。” + +子罕篇第九 + +  子罕言利,与命与仁。 + +  达巷党人曰:“大哉孔子!博学而无所成名。”子闻之,谓门弟子曰:“吾何执?执御乎,执射乎?吾执御矣。” + +  子曰:“麻冕,礼也;今也纯,俭,吾从众。拜下,礼也;今拜乎上,泰也;虽违众,吾从下。” + +  子绝四:毋意、毋必、毋固、毋我。 + +  子畏于匡,曰:“文王既没,文不在兹乎?天之将丧斯文也,后死者不得与于斯文也;天之未丧斯文也,匡人其如予何?” + +  太宰问于子贡曰:“夫子圣者与,何其多能也?”子贡曰:“固天纵之将圣,又多能也。”子闻之,曰:“太宰知我乎?吾少也贱,故多能鄙事。君子多乎哉?不多也。” + +  牢曰:“子云:‘吾不试,故艺。’” + +  子曰:“吾有知乎哉?无知也。有鄙夫问于我,空空如也。我叩其两端而竭焉。” + +  子曰:“凤鸟不至,河不出图,吾已矣夫!” + +  子见齐衰者、冕衣裳者与瞽者,见之,虽少,必作,过之必趋。 + +  颜渊喟然叹曰:“仰之弥高,钻之弥坚。瞻之在前,忽焉在后。夫子循循然善诱人,博我以文,约我以礼,欲罢不能。既竭吾才,如有所立卓尔,虽欲从之,末由也已。” + +  子疾病,子路使门人为臣。病间,曰:“久矣哉,由之行诈也!无臣而为有臣,吾谁欺?欺天乎?且予与其死于臣之手也,无宁死于二三子之手乎!且予纵不得大葬,予死于道路乎?” + +  子贡曰:“有美玉于斯,韫椟而藏诸?求善贾而沽诸?”子曰:“沽之哉,沽之哉!我待贾者也。” + +  子欲居九夷。或曰:“陋,如之何?”子曰:“君子居之,何陋之有!” + +  子曰:“吾自卫反鲁,然后乐正,《雅》《颂》各得其所。” + +  子曰:“出则事公卿,入则事父兄,丧事不敢不勉,不为酒困,何有于我哉?” + +  子在川上曰:“逝者如斯夫!不舍昼夜。” + +  子曰:“吾未见好德如好色者也。” + +  子曰:“譬如为山,未成一篑,止,吾止也;譬如平地,虽覆一篑,进,吾往也。” + +  子曰:“语之而不惰者,其回也与!” + +  子谓颜渊,曰:“惜乎!吾见其进也,未见其止也。” + +  子曰:“苗而不秀者有矣夫,秀而不实者有矣夫。” + +  子曰:“后生可畏,焉知来者之不如今也?四十、五十而无闻焉,斯亦不足畏也已。” + +  子曰:“法语之言,能无从乎?改之为贵。巽与之言,能无说乎?绎之为贵。说而不绎,从而不改,吾末如之何也已矣。” + +  子曰:“主忠信。毋友不如己者,过,则勿惮改。” + +  子曰:“三军可夺帅也,匹夫不可夺志也。” + +  子曰:“衣敝缊袍,与衣狐貉者立而不耻者,其由也与!‘不忮不求,何用不臧?’”子路终身诵之,子曰:“是道也,何足以臧?” + +  子曰:“岁寒,然后知松柏之后凋也。” + +  子曰:“知者不惑,仁者不忧,勇者不惧。” + +  子曰:“可与共学,未可与适道;可与适道,未可与立;可与立,未可与权。” + +  “唐棣之华,偏其反而。岂不尔思?室是远尔。”子曰:“未之思也,夫何远之有。” + +乡党篇第十 + +  孔子于乡党,恂恂如也,似不能言者;其在宗庙朝庭,便便言,唯谨尔。 + +  朝,与下大夫言,侃侃如也;与上大夫言,訚訚如也。君在,踧踖如也,与与如也。 + +  君召使摈,色勃如也,足躩如也。揖所与立,左右手,衣前后,襜如也。趋进,翼如也。宾退,必复命曰:“宾不顾矣。” + +  入公门,鞠躬如也,如不容。立不中门,行不履阈。过位,色勃如也,足躩如也,其言似不足者。摄齐升堂,鞠躬如也,屏气似不息者。出,降一等,逞颜色,怡怡如也;没阶,趋进,翼如也;复其位,踧踖如也。 + +  执圭,鞠躬如也,如不胜。上如揖,下如授。勃如战色,足蹜蹜如有循。享礼,有容色。私觌,愉愉如也。 + +  君子不以绀緅饰,红紫不以为亵服。当暑袗絺绤,必表而出之。缁衣羔裘,素衣麑裘,黄衣狐裘。亵裘长,短右袂。必有寝衣,长一身有半。狐貉之厚以居。去丧,无所不佩。非帷裳,必杀之。羔裘玄冠不以吊。吉月,必朝服而朝。 + +  齐,必有明衣,布。齐必变食,居必迁坐。 + +  食不厌精,脍不厌细。食饐而餲,鱼馁而肉败,不食;色恶,不食;臭恶,不食;失饪,不食;不时,不食;割不正,不食;不得其酱,不食。肉虽多,不使胜食气。唯酒无量,不及乱。沽酒市脯,不食。不撤姜食,不多食。 + +  祭于公,不宿肉。祭肉不出三日,出三日不食之矣。 + +  食不语,寝不言。 + +  虽疏食菜羹,瓜祭,必齐如也。 + +  席不正,不坐。 + +  乡人饮酒,杖者出,斯出矣。 + +  乡人傩,朝服而立于阼阶。 + +  问人于他邦,再拜而送之。 + +  康子馈药,拜而受之。曰:“丘未达,不敢尝。” + +  厩焚,子退朝,曰:“伤人乎?”不问马。 + +  君赐食,必正席先尝之;君赐腥,必熟而荐之;君赐生,必畜之。侍食于君,君祭,先饭。 + +  疾,君视之,东首,加朝服,拖绅。 + +  君命召,不俟驾行矣。 + +  入太庙,每事问。 + +  朋友死,无所归,曰:“于我殡。” + +  朋友之馈,虽车马,非祭肉,不拜。 + +  寝不尸,居不容。(居不容 一作:居不客) + +  见齐衰者,虽狎,必变。见冕者与瞽者,虽亵,必以貌。凶服者式之,式负版者。有盛馔,必变色而作。迅雷风烈,必变。 + +  升车,必正立,执绥。车中不内顾,不疾言,不亲指。 + +  色斯举矣,翔而后集。曰:“山梁雌雉,时哉时哉!”子路共之,三嗅而作。 + +先进篇第十一 + +  子曰:“先进于礼乐,野人也;后进于礼乐,君子也。如用之,则吾从先进。” + +  子曰:“从我于陈、蔡者,皆不及门也。” + +  德行:颜渊,闵子骞,冉伯牛,仲弓。言语:宰我,子贡。政事:冉有,季路。文学:子游,子夏。 + +  子曰:“回也非助我者也,于吾言无所不说。” + +  子曰:“孝哉闵子骞!人不间于其父母昆弟之言。” + +  南容三复白圭,孔子以其兄之子妻之。 + +  季康子问:“弟子孰为好学?”孔子对曰:“有颜回者好学,不幸短命死矣,今也则亡。” + +  颜渊死,颜路请子之车以为之椁。子曰:“才不才,亦各言其子也。鲤也死,有棺而无椁,吾不徒行以为之椁。以吾从大夫之后,不可徒行也。” + +  颜渊死。子曰:“噫!天丧予!天丧予!” + +  颜渊死,子哭之恸,从者曰:“子恸矣!”曰:“有恸乎?非夫人之为恸而谁为?” + +  颜渊死,门人欲厚葬之,子曰:“不可。”门人厚葬之,子曰:“回也视予犹父也,予不得视犹子也。非我也,夫二三子也!” + +  季路问事鬼神,子曰:“未能事人,焉能事鬼?”曰:“敢问死。”曰:“未知生,焉知死?” + +  闵子侍侧,訚訚如也;子路,行行如也;冉有、子贡,侃侃如也。子乐。“若由也,不得其死然。” + +  鲁人为长府,闵子骞曰:“仍旧贯如之何?何必改作?”子曰:“夫人不言,言必有中。” + +  子曰:“由之瑟奚为于丘之门?”门人不敬子路,子曰:“由也升堂矣,未入于室也。” + +  子贡问:“师与商也孰贤?”子曰:“师也过,商也不及。”曰:“然则师愈与?”子曰:“过犹不及。” + +  季氏富于周公,而求也为之聚敛而附益之。子曰:“非吾徒也,小子鸣鼓而攻之可也。” + +  柴也愚,参也鲁,师也辟,由也喭。 + +  子曰:“回也其庶乎,屡空。赐不受命而货殖焉,亿则屡中。” + +  子张问善人之道,子曰:“不践迹,亦不入于室。” + +  子曰:“论笃是与,君子者乎,色庄者乎?” + +  子路问:“闻斯行诸?”子曰:“有父兄在,如之何其闻斯行之?”冉有问:“闻斯行诸?”子曰:“闻斯行之。公西华曰:“由也问闻斯行诸,子曰‘有父兄在’;求也问闻斯行诸,子曰‘闻斯行之’。赤也惑,敢问。”子曰:“求也退,故进之;由也兼人,故退之。” + +  子畏于匡,颜渊后。子曰:“吾以女为死矣!”曰:“子在,回何敢死!” + +  季子然问:“仲由、冉求可谓大臣与?”子曰:“吾以子为异之问,曾由与求之问。所谓大臣者,以道事君,不可则止。今由与求也,可谓具臣矣。”曰:“然则从之者与?”子曰:“弑父与君,亦不从也。” + +  子路使子羔为费宰,子曰:“贼夫人之子。”子路曰:“有民人焉,有社稷焉,何必读书然后为学。”子曰:“是故恶夫佞者。” + +  子路、曾皙、冉有、公西华侍坐,子曰:“以吾一日长乎尔,毋吾以也。居则曰‘不吾知也’如或知尔,则何以哉?”子路率尔而对曰:“千乘之国,摄乎大国之间,加之以师旅,因之以饥馑,由也为之,比及三年,可使有勇,且知方也。”夫子哂之。“求,尔何如?”对曰:“方六七十,如五六十,求也为之,比及三年,可使足民。如其礼乐,以俟君子。”“赤!尔何如?”对曰:“非曰能之,愿学焉。宗庙之事,如会同,端章甫,愿为小相焉。”“点,尔何如?”鼓瑟希,铿尔,舍瑟而作,对曰:“异乎三子者之撰。”子曰:“何伤乎?亦各言其志也。”曰:“暮春者,春服既成,冠者五六人,童子六七人,浴乎沂,风乎舞雩,咏而归。”夫子喟然叹曰:“吾与点也!”三子者出,曾皙后。曾皙曰:“夫三子者之言何如?”子曰:“亦各言其志也已矣。”曰:“夫子何哂由也?”曰:“为国以礼,其言不让,是故哂之。”“唯求则非邦也与?”“安见方六七十、如五六十而非邦也者?”“唯赤则非邦也与?”“宗庙会同,非诸侯而何?赤也为之小,孰能为之大?” + +颜渊篇第十二 + +  颜渊问仁,子曰:“克己复礼为仁。一日克己复礼,天下归仁焉。为仁由己,而由人乎哉?”颜渊曰:“请问其目?”子曰:“非礼勿视,非礼勿听,非礼勿言,非礼勿动。”颜渊曰:“回虽不敏,请事斯语矣。” + +  仲弓问仁,子曰:“出门如见大宾,使民如承大祭。己所不欲,勿施于人。在邦无怨,在家无怨。”仲弓曰:“雍虽不敏,请事斯语矣。” + +  司马牛问仁,子曰:“仁者,其言也讱。”曰:“其言也讱,斯谓之仁已乎?”子曰:“为之难,言之得无讱乎?” + +  司马牛问君子,子曰:“君子不忧不惧。”曰:“不忧不惧,斯谓之君子已乎?”子曰:“内省不疚,夫何忧何惧?” + +  司马牛忧曰:“人皆有兄弟,我独亡。”子夏曰:“商闻之矣:死生有命,富贵在天。君子敬而无失,与人恭而有礼,四海之内皆兄弟也。君子何患乎无兄弟也?” + +  子张问明,子曰:“浸润之谮,肤受之愬,不行焉,可谓明也已矣;浸润之谮、肤受之愬不行焉,可谓远也已矣。” + +  子贡问政,子曰:“足食,足兵,民信之矣。”子贡曰:“必不得已而去,于斯三者何先?”曰:“去兵。”子贡曰:“必不得已而去,于斯二者何先?”曰:“去食。自古皆有死,民无信不立。” + +  棘子成曰:“君子质而已矣,何以文为?”子贡曰:“惜乎,夫子之说君子也!驷不及舌。文犹质也,质犹文也。虎豹之鞟犹犬羊之鞟。” + +  哀公问于有若曰:“年饥,用不足,如之何?”有若对曰:“盍彻乎?”曰:“二,吾犹不足,如之何其彻也?”对曰:“百姓足,君孰与不足?百姓不足,君孰与足?” + +  子张问崇德辨惑,子曰:“主忠信,徙义,崇德也。爱之欲其生,恶之欲其死;既欲其生又欲其死,是惑也。‘诚不以富,亦只以异。’” + +  齐景公问政于孔子,孔子对曰:“君君,臣臣,父父,子子。”公曰:“善哉!信如君不君、臣不臣、父不父、子不子,虽有粟,吾得而食诸?” + +  子曰:“片言可以折狱者,其由也与?”子路无宿诺。 + +  子曰:“听讼,吾犹人也。必也使无讼乎。” + +  子张问政,子曰:“居之无倦,行之以忠。” + +  子曰:“博学于文,约之以礼,亦可以弗畔矣夫。” + +  子曰:“君子成人之美,不成人之恶;小人反是。” + +  季康子问政于孔子,孔子对曰:“政者,正也。子帅以正,孰敢不正?” + +  季康子患盗,问于孔子。孔子对曰:“苟子之不欲,虽赏之不窃。” + +  季康子问政于孔子曰:“如杀无道以就有道,何如?”孔子对曰:“子为政,焉用杀?子欲善而民善矣。君子之德风,小人之德草,草上之风必偃。” + +  子张问:“士何如斯可谓之达矣?”子曰:“何哉尔所谓达者?”子张对曰:“在邦必闻,在家必闻。”子曰:“是闻也,非达也。夫达也者,质直而好义,察言而观色,虑以下人。在邦必达,在家必达。夫闻也者,色取仁而行违,居之不疑。在邦必闻,在家必闻。” + +  樊迟从游于舞雩之下,曰:“敢问崇德、修慝、辨惑。”子曰:“善哉问!先事后得,非崇德与?攻其恶,无攻人之恶,非修慝与?一朝之忿,忘其身,以及其亲,非惑与?” + +  樊迟问仁,子曰:“爱人。”问知,子曰:“知人。”樊迟未达,子曰:“举直错诸枉,能使枉者直。”樊迟退,见子夏,曰:“乡也吾见于夫子而问知,子曰:‘举直错诸枉,能使枉者直’,何谓也?”子夏曰:“富哉言乎!舜有天下,选于众,举皋陶,不仁者远矣。汤有天下,选于众,举伊尹,不仁者远矣。” + +  子贡问友,子曰:“忠告而善道之,不可则止,毋自辱焉。” + +  曾子曰:“君子以文会友,以友辅仁。” + +子路篇第十三 + +  子路问政,子曰:“先之,劳之。”请益,曰:“无倦。” + +  仲弓为季氏宰,问政,子曰:“先有司,赦小过,举贤才。”曰:“焉知贤才而举之?”曰:“举尔所知。尔所不知,人其舍诸?” + +  子路曰:“卫君待子而为政,子将奚先?”子曰:“必也正名乎!”子路曰:“有是哉,子之迂也!奚其正?”子曰:“野哉由也!君子于其所不知,盖阙如也。名不正,则言不顺;言不顺,则事不成;事不成,则礼乐不兴;礼乐不兴,则刑罚不中;刑罚不中,则民无所措手足。故君子名之必可言也,言之必可行也。君子于其言,无所苟而已矣。” + +  樊迟请学稼,子曰:“吾不如老农。”请学为圃,曰:“吾不如老圃。”樊迟出,子曰:“小人哉樊须也!上好礼,则民莫敢不敬;上好义,则民莫敢不服;上好信,则民莫敢不用情。夫如是,则四方之民襁负其子而至矣,焉用稼?” + +  子曰:“诵《诗》三百,授之以政,不达;使于四方,不能专对;虽多,亦奚以为?” + +  子曰:“其身正,不令而行;其身不正,虽令不从。” + +  子曰:“鲁卫之政,兄弟也。” + +  子谓卫公子荆,“善居室。始有,曰:‘苟合矣。’少有,曰:‘苟完矣。’富有,曰:‘苟美矣。’” + +  子适卫,冉有仆,子曰:“庶矣哉!”冉有曰:“既庶矣,又何加焉?”曰:“富之。”曰:“既富矣,又何加焉?”曰:“教之。” + +  子曰:“苟有用我者,期月而已可也,三年有成。” + +  子曰:“‘善人为邦百年,亦可以胜残去杀矣。’诚哉是言也!” + +  子曰:“如有王者,必世而后仁。” + +  子曰:“苟正其身矣,于从政乎何有?不能正其身,如正人何?” + +  冉子退朝,子曰:“何晏也?”对曰:“有政。”子曰:“其事也。如有政,虽不吾以,吾其与闻之。” + +  定公问:“一言而可以兴邦,有诸?”孔子对曰:“言不可以若是,其几也。人之言曰:‘为君难,为臣不易。’如知为君之难也,不几乎一言而兴邦乎?”曰:“一言而丧邦,有诸?”孔子对曰:“言不可以若是其几也。人之言曰:‘予无乐乎为君,唯其言而莫予违也。’如其善而莫之违也,不亦善乎?如不善而莫之违也,不几乎一言而丧邦乎?” + +  叶公问政,子曰:“近者说,远者来。” + +  子夏为莒父宰,问政,子曰:“无欲速,无见小利。欲速则不达,见小利则大事不成。” + +  叶公语孔子曰:“吾党有直躬者,其父攘羊,而子证之。”孔子曰:“吾党之直者异于是。父为子隐,子为父隐,直在其中矣。” + +  樊迟问仁,子曰:“居处恭,执事敬,与人忠。虽之夷狄,不可弃也。” + +  子贡问曰:“何如斯可谓之士矣?”子曰:“行己有耻,使于四方不辱君命,可谓士矣。”曰:“敢问其次。”曰:“宗族称孝焉,乡党称弟焉。”曰:“敢问其次。”曰:“言必信,行必果,硁硁然小人哉!抑亦可以为次矣。”曰:“今之从政者何如?”子曰:“噫!斗筲之人,何足算也!” + +  子曰:“不得中行而与之,必也狂狷乎!狂者进取,狷者有所不为也。” + +  子曰:“南人有言曰:‘人而无恒,不可以作巫医。’善夫!”“不恒其德,或承之羞。”子曰:“不占而已矣。” + +  子曰:“君子和而不同,小人同而不和。” + +  子贡问曰:“乡人皆好之,何如?”子曰:“未可也。”“乡人皆恶之,何如?”子曰:“未可也。不如乡人之善者好之,其不善者恶之。” + +  子曰:“君子易事而难说也,说之不以道不说也,及其使人也器之;小人难事而易说也,说之虽不以道说也,及其使人也求备焉。” + +  子曰:“君子泰而不骄,小人骄而不泰。” + +  子曰:“刚、毅、木、讷近仁。” + +  子路问曰:“何如斯可谓之士矣?”子曰:“切切偲偲,怡怡如也,可谓士矣。朋友切切偲偲,兄弟怡怡。” + +  子曰:“善人教民七年,亦可以即戎矣。” + +  子曰:“以不教民战,是谓弃之。” + +宪问篇第十四 + +  宪问耻,子曰:“邦有道,谷;邦无道,谷,耻也。”“克、伐、怨、欲不行焉,可以为仁矣?”子曰:“可以为难矣,仁则吾不知也。” + +  子曰:“士而怀居,不足以为士矣。” + +  子曰:“邦有道,危言危行;邦无道,危行言孙。” + +  子曰:“有德者必有言,有言者不必有德。仁者必有勇,勇者不必有仁。” + +  南宫适问于孔子曰:“羿善射,奡荡舟,俱不得其死然;禹、稷躬稼而有天下。”夫子不答。南宫适出,子曰:“君子哉若人!尚德哉若人!” + +  子曰:“君子而不仁者有矣夫,未有小人而仁者也。” + +  子曰:“爱之,能勿劳乎?忠焉,能勿诲乎?” + +  子曰:“为命,裨谌草创之,世叔讨论之,行人子羽修饰之,东里子产润色之。” + +  或问子产,子曰:“惠人也。”问子西,曰:“彼哉,彼哉!”问管仲,曰:“人也。夺伯氏骈邑三百,饭疏食,没齿无怨言。” + +  子曰:“贫而无怨难,富而无骄易。” + +  子曰:“孟公绰为赵、魏老则优,不可以为滕、薛大夫。” + +  子路问成人,子曰:“若臧武仲之知、公绰之不欲、卞庄子之勇、冉求之艺,文之以礼乐,亦可以为成人矣。”曰:“今之成人者何必然?见利思义,见危授命,久要不忘平生之言,亦可以为成人矣。” + +  子问公叔文子于公明贾曰:“信乎,夫子不言,不笑,不取乎?”公明贾对曰:“以告者过也。夫子时然后言,人不厌其言;乐然后笑,人不厌其笑;义然后取,人不厌其取。”子曰:“其然?岂其然乎?” + +  子曰:“臧武仲以防求为后于鲁,虽曰不要君,吾不信也。” + +  子曰:“晋文公谲而不正,齐桓公正而不谲。” + +  子路曰:“桓公杀公子纠,召忽死之,管仲不死,曰未仁乎?”子曰:“桓公九合诸侯不以兵车,管仲之力也。如其仁,如其仁!” + +  子贡曰:“管仲非仁者与?桓公杀公子纠,不能死,又相之。”子曰:“管仲相桓公霸诸侯,一匡天下,民到于今受其赐。微管仲,吾其被发左衽矣。岂若匹夫匹妇之为谅也,自经于沟渎而莫之知也。” + +  公叔文子之臣大夫僎与文子同升诸公,子闻之,曰:“可以为‘文’矣。” + +  子言卫灵公之无道也,康子曰:“夫如是,奚而不丧?”孔子曰:“仲叔圉治宾客,祝鮀治宗庙,王孙贾治军旅,夫如是,奚其丧?” + +  子曰:“其言之不怍,则为之也难。” + +  陈成子弑简公,孔子沐浴而朝,告于哀公曰:“陈恒弑其君,请讨之。”公曰:“告夫三子。”,孔子曰:“以吾从大夫之后,不敢不告也,君曰‘告夫三子’者!”之三子告,不可。孔子曰:“以吾从大夫之后,不敢不告也。” + +  子路问事君,子曰:“勿欺也,而犯之。” + +  子曰:“君子上达,小人下达。” + +  子曰:“古之学者为己,今之学者为人。” + +  蘧伯玉使人于孔子,孔子与之坐而问焉,曰:“夫子何为?”对曰:“夫子欲寡其过而未能也。”使者出,子曰:“使乎!使乎!” + +  子曰:“不在其位,不谋其政。”曾子曰:“君子思不出其位。” + +  子曰:“君子耻其言而过其行。” + +  子曰:“君子道者三,我无能焉:仁者不忧,知者不惑,勇者不惧。”子贡曰:“夫子自道也。” + +  子贡方人,子曰:“赐也贤乎哉?夫我则不暇。” + +  子曰:“不患人之不己知,患其不能也。” + +  子曰:“不逆诈,不亿不信,抑亦先觉者,是贤乎!” + +  微生亩谓孔子曰:“丘何为是栖栖者与?无乃为佞乎?”孔子曰:“非敢为佞也,疾固也。” + +  子曰:“骥不称其力,称其德也。” + +  或曰:“以德报怨,何如?”子曰:“何以报德?以直报怨,以德报德。” + +  子曰:“莫我知也夫!”子贡曰:“何为其莫知子也?”子曰:“不怨天,不尤人,下学而上达。知我者其天乎!” + +  公伯寮愬子路于季孙。子服景伯以告,曰:“夫子固有惑志于公伯寮,吾力犹能肆诸市朝。”子曰:“道之将行也与,命也;道之将废也与,命也。公伯寮其如命何?” + +  子曰:“贤者辟世,其次辟地,其次辟色,其次辟言。”子曰:“作者七人矣。” + +  子路宿于石门,晨门曰:“奚自?”子路曰:“自孔氏。”曰:“是知其不可而为之者与?” + +  子击磬于卫,有荷蒉而过孔氏之门者,曰:“有心哉,击磬乎!”既而曰:“鄙哉,硁硁乎!莫己知也,斯己而已矣。深则厉,浅则揭。”子曰:“果哉!末之难矣。” + +  子张曰:“《书》云,‘高宗谅阴,三年不言。’何谓也?”子曰:“何必高宗,古之人皆然。君薨,百官总己以听于冢宰三年。” + +  子曰:“上好礼,则民易使也。” + +  子路问君子,子曰:“修己以敬。”曰:“如斯而已乎?”曰:“修己以安人。”曰:“如斯而已乎?”曰:“修己以安百姓。修己以安百姓,尧、舜其犹病诸!” + +  原壤夷俟,子曰:“幼而不孙弟,长而无述焉,老而不死,是为贼!”以杖叩其胫。 + +  阙党童子将命,或问之曰:“益者与?”子曰:“吾见其居于位也,见其与先生并行也。非求益者也,欲速成者也。” + +卫灵公篇第十五 + +  卫灵公问陈于孔子,孔子对曰:“俎豆之事,则尝闻之矣;军旅之事,未之学也。”明日遂行。 + +  在陈绝粮,从者病莫能兴。子路愠见曰:“君子亦有穷乎?”子曰:“君子固穷,小人穷斯滥矣。” + +  子曰:“赐也,女以予为多学而识之者与?”对曰:“然,非与?”曰:“非也,予一以贯之。” + +  子曰:“由,知德者鲜矣。” + +  子曰:“无为而治者其舜也与!夫何为哉?恭己正南面而已矣。” + +  子张问行,子曰:“言忠信,行笃敬,虽蛮貊之邦行矣;言不忠信,行不笃敬,虽州里行乎哉?立则见其参于前也;在舆则见其倚于衡也,夫然后行。”子张书诸绅。 + +  子曰:“直哉史鱼!邦有道如矢,邦无道如矢。君子哉蘧伯玉!邦有道则仕,邦无道则可卷而怀之。” + +  子曰:“可与言而不与之言,失人;不可与言而与之言,失言。知者不失人亦不失言。” + +  子曰:“志士仁人无求生以害仁,有杀身以成仁。” + +  子贡问为仁,子曰:“工欲善其事,必先利其器。居是邦也,事其大夫之贤者,友其士之仁者。” + +  颜渊问为邦,子曰:“行夏之时,乘殷之辂,服周之冕,乐则《韶》《舞》;放郑声,远佞人。郑声淫,佞人殆。” + +  子曰:“人无远虑,必有近忧。” + +  子曰:“已矣乎!吾未见好德如好色者也。” + +  子曰:“臧文仲其窃位者与!知柳下惠之贤而不与立也。” + +  子曰:“躬自厚而薄责于人,则远怨矣。” + +  子曰:“不曰‘如之何、如之何’者,吾末如之何也已矣。” + +  子曰:“群居终日,言不及义,好行小慧,难矣哉!” + +  子曰:“君子义以为质,礼以行之,孙以出之,信以成之。君子哉!” + +  子曰:“君子病无能焉,不病人之不己知也。” + +  子曰:“君子疾没世而名不称焉。” + +  子曰:“君子求诸己,小人求诸人。” + +  子曰:“君子矜而不争,群而不党。” + +  子曰:“君子不以言举人,不以人废言。” + +  子贡问曰:“有一言而可以终身行之者乎?”子曰:“其恕乎!己所不欲,勿施于人。” + +  子曰:“吾之于人也,谁毁谁誉?如有所誉者,其有所试矣。斯民也,三代之所以直道而行也。” + +  子曰:“吾犹及史之阙文也,有马者借人乘之,今亡矣夫!” + +  子曰:“巧言乱德,小不忍,则乱大谋。” + +  子曰:“众恶之,必察焉;众好之,必察焉。” + +  子曰:“人能弘道,非道弘人。” + +  子曰:“过而不改,是谓过矣。” + +  子曰:“吾尝终日不食、终夜不寝以思,无益,不如学也。” + +  子曰:“君子谋道不谋食。耕也馁在其中矣,学也禄在其中矣。君子忧道不忧贫。” + +  子曰:“知及之,仁不能守之,虽得之,必失之;知及之,仁能守之,不庄以莅之,则民不敬;知及之,仁能守之,庄以莅之,动之不以礼,未善也。” + +  子曰:“君子不可小知而可大受也,小人不可大受而可小知也。” + +  子曰:“民之于仁也,甚于水火。水火,吾见蹈而死者矣,未见蹈仁而死者也。” + +  子曰:“当仁不让于师。” + +  子曰:“君子贞而不谅。” + +  子曰:“事君,敬其事而后其食。” + +  子曰:“有教无类。” + +  子曰:“道不同,不相为谋。” + +  子曰:“辞达而已矣。” + +  师冕见,及阶,子曰:“阶也。”及席,子曰:“席也。”皆坐,子告之曰:“某在斯,某在斯。”师冕出。子张问曰:“与师言之道与?”子曰:“然,固相师之道也。” + +季氏篇第十六 + +  季氏将伐颛臾。冉有、季路见于孔子曰: “季氏将有事于颛臾。 ”孔子曰: “求!无乃尔是过与?夫颛臾,昔者先王以为东蒙主,且在邦域之中矣,是社稷之臣也。何以伐为? ”冉有曰: “夫子欲之,吾二臣者皆不欲也。 ”孔子曰: “求!周任有言曰:‘陈力就列,不能者止。’危而不持,颠而不扶,则将焉用彼相矣?且尔言过矣,虎兕出于柙,龟玉毁于椟中,是谁之过与? ”冉有曰: “今夫颛臾,固而近于费。今不取,后世必为子孙忧。 ”孔子曰: “求!君子疾夫舍曰欲之而必为之辞。丘也闻有国有家者,不患寡而患不均,不患贫而患不安。盖均无贫,和无寡,安无倾。夫如是,故远人不服,则修文德以来之。既来之,则安之。今由与求也,相夫子,远人不服而不能来也,邦分崩离析而不能守也;而谋动干戈于邦内。吾恐季孙之忧,不在颛臾,而在萧墙之内也。 ” + +  孔子曰: “天下有道,则礼乐征伐自天子出;天下无道,则礼乐征伐自诸侯出。自诸侯出,盖十世希不失矣;自大夫出,五世希不失矣;陪臣执国命,三世希不失矣。天下有道,则政不在大夫。天下有道,则庶人不议。 ” + +  孔子曰: “禄之去公室五世矣,政逮于大夫四世矣,故夫三桓之子孙微矣。 ” + +  孔子曰: “益者三友,损者三友。友直,友谅,友多闻,益矣。友便辟,友善柔,友便佞,损矣。 ” + +  孔子曰: “益者三乐,损者三乐。乐节礼乐,乐道人之善,乐多贤友,益矣。乐骄乐,乐佚游,乐晏乐,损矣。 ” + +  孔子曰: “侍于君子有三愆:言未及之而言谓之躁,言及之而不言谓之隐,未见颜色而言谓之瞽。 ” + +  孔子曰: “君子有三戒:少之时,血气未定,戒之在色;及其壮也,血气方刚,戒之在斗;及其老也,血气既衰,戒之在得。 ” + +  孔子曰: “君子有三畏:畏天命,畏大人,畏圣人之言。小人不知天命而不畏也,狎大人,侮圣人之言。 ” + +  孔子曰: “生而知之者,上也;学而知之者,次也;困而学之,又其次也;困而不学,民斯为下矣。 ” + +  孔子曰: “君子有九思:视思明,听思聪,色思温,貌思恭,言思忠,事思敬,疑思问,忿思难,见得思义。 ” + +  子曰: “见善如不及,见不善如探汤。吾见其人矣,吾闻其语矣。隐居以求其志,行义以达其道。吾闻其语矣,未见其人也。 ” + +  齐景公有马千驷,死之日,民无德而称焉。伯夷叔齐饿于首阳之下,民到于今称之。其斯之谓与? + +  陈亢问于伯鱼曰: “子亦有异闻乎? ”对曰: “未也。尝独立,鲤趋而过庭。曰:‘学诗乎?’对曰:‘未也’。‘不学诗,无以言。’鲤退而学诗。他日又独立,鲤趋而过庭。曰:‘学礼乎?’对曰:‘未也’。‘不学礼,无以立。’鲤退而学礼。闻斯二者。 ”陈亢退而喜曰: “问一得三:闻诗,闻礼,又闻君子之远其子也。 ” + +  邦君之妻,君称之曰“夫人” ,夫人自称曰“小童” ;邦人称之曰“君夫人” ,称诸异邦曰“寡小君” ;异邦人称之亦曰“君夫人” 。 + +阳货篇第十七 + +  阳货欲见孔子,孔子不见,归孔子豚。孔子时其亡也而往拜之,遇诸涂。谓孔子曰:“来,予与尔言。”曰:“怀其宝而迷其邦,可谓仁乎?”曰:“不可。”“好从事而亟失时,可谓知乎?”曰:“不可!”“日月逝矣,岁不我与!”孔子曰:“诺,吾将仕矣。” + +  子曰:“性相近也,习相远也。” + +  子曰:“唯上知与下愚不移。” + +  子之武城,闻弦歌之声。夫子莞尔而笑,曰:“割鸡焉用牛刀?”子游对曰:“昔者偃也闻诸夫子曰:‘君子学道则爱人,小人学道则易使也。’”子曰:“二三子,偃之言是也!前言戏之耳。” + +  公山弗扰以费畔,召,子欲往。子路不说,曰:“末之也已,何必公山氏之之也?”子曰:“夫召我者而岂徒哉?如有用我者,吾其为东周乎!” + +  子张问仁于孔子,孔子曰:“能行五者于天下为仁矣。”请问之,曰:“恭、宽、信、敏、惠。恭则不侮,宽则得众,信则人任焉,敏则有功,惠则足以使人。” + +  佛肸召,子欲往。子路曰:“昔者由也闻诸夫子曰:‘亲于其身为不善者,君子不入也。’佛肸以中牟畔,子之往也,如之何?”子曰:“然,有是言也。不曰坚乎,磨而不磷;不曰白乎,涅而不缁。吾岂匏瓜也哉?焉能系而不食?” + +  子曰:“由也,女闻六言六蔽矣乎?”对曰:“未也。”“居!吾语女。好仁不好学,其蔽也愚;好知不好学,其蔽也荡;好信不好学,其蔽也贼;好直不好学,其蔽也绞;好勇不好学,其蔽也乱;好刚不好学,其蔽也狂。” + +  子曰:“小子何莫学夫《诗》?《诗》可以兴,可以观,可以群,可以怨。迩之事父,远之事君,多识于鸟兽草木之名。” + +  子谓伯鱼曰:“女为《周南》、《召南》矣乎?人而不为《周南》、《召南》,其犹正墙面而立也与!” + +  子曰:“礼云礼云,玉帛云乎哉?乐云乐云,钟鼓云乎哉?” + +  子曰:“色厉而内荏,譬诸小人,其犹穿窬之盗也与?” + +  子曰:“乡愿,德之贼也。” + +  子曰:“道听而涂说,德之弃也。” + +  子曰:“鄙夫可与事君也与哉?其未得之也,患得之;既得之,患失之。苟患失之,无所不至矣。” + +  子曰:“古者民有三疾,今也或是之亡也。古之狂也肆,今之狂也荡;古之矜也廉,今之矜也忿戾;古之愚也直,今之愚也诈而已矣。” + +  子曰:“巧言令色,鲜矣仁。” + +  子曰:“恶紫之夺朱也,恶郑声之乱雅乐也,恶利口之覆邦家者。” + +  子曰:“予欲无言。”子贡曰:“子如不言,则小子何述焉?”子曰:“天何言哉?四时行焉,百物生焉,天何言哉?” + +  孺悲欲见孔子,孔子辞以疾。将命者出户,取瑟而歌,使之闻之。 + +  宰我问:“三年之丧,期已久矣!君子三年不为礼,礼必坏;三年不为乐,乐必崩。旧谷既没,新谷既升,钻燧改火,期可已矣。”子曰:“食夫稻,衣夫锦,于女安乎?”曰:“安!”“女安则为之!夫君子之居丧,食旨不甘,闻乐不乐,居处不安,故不为也。今女安,则为之!”宰我出,子曰:“予之不仁也!子生三年,然后免于父母之怀。夫三年之丧,天下之通丧也,予也有三年之爱于其父母乎!” + +  子曰:“饱食终日,无所用心,难矣哉!不有博弈者乎?为之犹贤乎已。” + +  子路曰:“君子尚勇乎?”子曰:“君子义以为上。君子有勇而无义为乱,小人有勇而无义为盗。” + +  子贡曰:“君子亦有恶乎?”子曰:“有恶。恶称人之恶者,恶居下流而讪上者,恶勇而无礼者,恶果敢而窒者。”曰:“赐也亦有恶乎?”“恶徼以为知者,恶不孙以为勇者,恶讦以为直者。” + +  子曰:“唯女子与小人为难养也,近之则不逊,远之则怨。” + +  子曰:“年四十而见恶焉,其终也已。” + +微子篇第十八 + +  微子去之,箕子为之奴,比干谏而死。孔子曰:“殷有三仁焉。” + +  柳下惠为士师,三黜。人曰:“子未可以去乎?”曰:“直道而事人,焉往而不三黜?枉道而事人,何必去父母之邦?” + +  齐景公待孔子曰:“若季氏,则吾不能。”以季、孟之间待之,曰:“吾老矣,不能用也。”孔子行。 + +  齐人归女乐,季桓子受之,三日不朝,孔子行。 + +  楚狂接舆歌而过孔子曰:“凤兮凤兮,何德之衰?往者不可谏,来者犹可追。已而已而,今之从政者殆而!”孔子下,欲与之言,趋而辟之,不得与之言。 + +  长沮、桀溺耦而耕,孔子过之,使子路问津焉。长沮曰:“夫执舆者为谁?”子路曰:“为孔丘。”曰:“是鲁孔丘与?”曰:“是也。”曰:“是知津矣。”问于桀溺,桀溺曰:“子为谁?”曰:“为仲由。”曰:“是鲁孔丘之徒与?”对曰:“然。”曰:“滔滔者天下皆是也,而谁以易之?且而与其从辟人之士也,岂若从辟世之士?”耰而不辍。子路行以告,夫子怃然曰:“鸟兽不可与同群,吾非斯人之徒与而谁与?天下有道,丘不与易也。” + +  子路从而后,遇丈人,以杖荷蓧。子路问曰:“子见夫子乎?”丈人曰:“四体不勤,五谷不分,孰为夫子?”植其杖而芸,子路拱而立。止子路宿,杀鸡为黍而食之,见其二子焉。明日,子路行以告,子曰:“隐者也。”使子路反见之,至则行矣。子路曰:“不仕无义。长幼之节不可废也,君臣之义如之何其废之?欲洁其身而乱大伦。君子之仕也,行其义也,道之不行已知之矣。” + +  逸民:伯夷、叔齐、虞仲、夷逸、朱张、柳下惠、少连。子曰:“不降其志,不辱其身,伯夷、叔齐与!”谓:“柳下惠、少连降志辱身矣,言中伦,行中虑,其斯而已矣。”谓:“虞仲、夷逸隐居放言,身中清,废中权。我则异于是,无可无不可。” + +  太师挚适齐,亚饭干适楚,三饭缭适蔡,四饭缺适秦,鼓方叔入于河,播鼗武入于汉,少师阳、击磬襄入于海。 + +  周公谓鲁公曰:“君子不施其亲,不使大臣怨乎不以,故旧无大故则不弃也,无求备于一人。” + +  周有八士:伯达、伯适、仲突、仲忽、叔夜、叔夏、季随、季騧。 + +子张篇第十九 + +  子张曰:“士见危致命,见得思义,祭思敬,丧思哀,其可已矣。” + +  子张曰:“执德不弘,信道不笃,焉能为有?焉能为亡?” + +  子夏之门人问交于子张,子张曰:“子夏云何?”对曰:“子夏曰:‘可者与之,其不可者拒之。’”子张曰:“异乎吾所闻。君子尊贤而容众,嘉善而矜不能。我之大贤与,于人何所不容?我之不贤与,人将拒我,如之何其拒人也?” + +  子夏曰:“虽小道必有可观者焉,致远恐泥,是以君子不为也。” + +  子夏曰:“日知其所亡,月无忘其所能,可谓好学也已矣。” + +  子夏曰:“博学而笃志,切问而近思,仁在其中矣。” + +  子夏曰:“百工居肆以成其事,君子学以致其道。” + +  子夏曰:“小人之过也必文。” + +  子夏曰:“君子有三变:望之俨然,即之也温,听其言也厉。” + +  子夏曰:“君子信而后劳其民,未信,则以为厉己也;信而后谏,未信,则以为谤己也。” + +  子夏曰:“大德不逾闲,小德出入可也。” + +  子游曰:“子夏之门人小子,当洒扫应对进退则可矣。抑末也,本之则无,如之何?”子夏闻之,曰:“噫,言游过矣!君子之道,孰先传焉?孰后倦焉?譬诸草木,区以别矣。君子之道焉可诬也?有始有卒者,其惟圣人乎!” + +  子夏曰:“仕而优则学,学而优则仕。” + +  子游曰:“丧致乎哀而止。” + +  子游曰:“吾友张也为难能也,然而未仁。” + +  曾子曰:“堂堂乎张也,难与并为仁矣。” + +  曾子曰:“吾闻诸夫子,人未有自致者也,必也亲丧乎!” + +  曾子曰:“吾闻诸夫子,孟庄子之孝也,其他可能也;其不改父之臣与父之政,是难能也。” + +  孟氏使阳肤为士师,问于曾子。曾子曰:“上失其道,民散久矣。如得其情,则哀矜而勿喜!” + +  子贡曰:“纣之不善,不如是之甚也。是以君子恶居下流,天下之恶皆归焉。” + +  子贡曰:“君子之过也,如日月之食焉。过也,人皆见之;更也,人皆仰之。” + +  卫公孙朝问于子贡曰:“仲尼焉学?”子贡曰:“文武之道未坠于地,在人。贤者识其大者,不贤者识其小者,莫不有文武之道焉,夫子焉不学?而亦何常师之有?” + +  叔孙武叔语大夫于朝曰:“子贡贤于仲尼。”子服景伯以告子贡,子贡曰:“譬之宫墙,赐之墙也及肩,窥见室家之好;夫子之墙数仞,不得其门而入,不见宗庙之美、百官之富。得其门者或寡矣,夫子之云不亦宜乎!” + +  叔孙武叔毁仲尼,子贡曰:“无以为也,仲尼不可毁也。他人之贤者,丘陵也,犹可逾也;仲尼,日月也,无得而逾焉。人虽欲自绝,其何伤于日月乎?多见其不知量也。” + +  陈子禽谓子贡曰:“子为恭也,仲尼岂贤于子乎?”子贡曰:“君子一言以为知,一言以为不知,言不可不慎也。夫子之不可及也,犹天之不可阶而升也。夫子之得邦家者,所谓立之斯立,道之斯行,绥之斯来,动之斯和。其生也荣,其死也哀,如之何其可及也?” + +尧曰篇第二十 + +  尧曰:“咨!尔舜,天之历数在尔躬,允执其中。四海困穷,天禄永终。”舜亦以命禹。曰:“予小子履,敢用玄牡,敢昭告于皇皇后帝:有罪不敢赦,帝臣不蔽,简在帝心。朕躬有罪,无以万方;万方有罪,罪在朕躬。”周有大赉,善人是富。“虽有周亲,不如仁人。百姓有过,在予一人。”谨权量,审法度,修废官,四方之政行焉。兴灭国,继绝世,举逸民,天下之民归心焉。所重:民、食、丧、祭。宽则得众,信则民任焉,敏则有功,公则说。 + +  子张问于孔子曰:“何如斯可以从政矣?”子曰:“尊五美,屏四恶,斯可以从政矣。”子张曰:“何谓五美?”子曰:“君子惠而不费,劳而不怨,欲而不贪,泰而不骄,威而不猛。”子张曰:“何谓惠而不费?”子曰:“因民之所利而利之,斯不亦惠而不费乎?择可劳而劳之,又谁怨?欲仁而得仁,又焉贪?君子无众寡,无小大,无敢慢,斯不亦泰而不骄乎?君子正其衣冠,尊其瞻视,俨然人望而畏之,斯不亦威而不猛乎?”子张曰:“何谓四恶?”子曰:“不教而杀谓之虐;不戒视成谓之暴;慢令致期谓之贼;犹之与人也,出纳之吝,谓之有司。” + +  孔子曰:“不知命,无以为君子也;不知礼,无以立也;不知言,无以知人也。”